feat(interrupts): add support for GICv3 controller
Most of the source code is taken from trustedfirmware-a project and
put in gicv3_helpers.h; We made some changes to fit to hafnium project
Moreover, TF-A(BL31) also configures GIC Distributor, Redistributor and
GIC CPU interfaces while booting CPUs. Hence, many of the configuration
steps are skipped in the current GICv3 driver implementation.
Change-Id: Ie17ab7c65578866a56fa054cd1fea9cd6a0f32a4
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/arch/aarch64/plat/interrupts/gicv3.c b/src/arch/aarch64/plat/interrupts/gicv3.c
index e53ba6c..d48dd2b 100644
--- a/src/arch/aarch64/plat/interrupts/gicv3.c
+++ b/src/arch/aarch64/plat/interrupts/gicv3.c
@@ -6,24 +6,480 @@
* https://opensource.org/licenses/BSD-3-Clause.
*/
+#include "hf/cpu.h"
+#include "hf/dlog.h"
+#include "hf/interrupt_desc.h"
+#include "hf/io.h"
+#include "hf/panic.h"
#include "hf/plat/interrupts.h"
+#include "hf/static_assert.h"
#include "hf/types.h"
+#include "gicv3_helpers.h"
#include "msr.h"
+#define GICD_SIZE (0x10000)
+#define GICV3_REDIST_SIZE_PER_PE (0x20000) /* 128 KB */
+
+#define REDIST_LAST_FRAME_MASK (1 << 4)
+#define GICV3_REDIST_FRAMES_OFFSET GICV3_REDIST_SIZE_PER_PE
+
+struct gicv3_driver {
+ uintptr_t dist_base;
+ uintptr_t base_redist_frame;
+ uintptr_t all_redist_frames[MAX_CPUS];
+ struct spinlock lock;
+};
+
+static struct gicv3_driver plat_gicv3_driver;
+
+static uint32_t affinity_to_core_id(uint64_t reg)
+{
+ struct cpu *this_cpu;
+
+ this_cpu = cpu_find(reg & MPIDR_AFFINITY_MASK);
+
+ CHECK(this_cpu != NULL);
+
+ return cpu_index(this_cpu);
+}
+
+/**
+ * This function checks the interrupt ID and returns true for SGIs and (E)PPIs
+ * and false for (E)SPIs IDs.
+ */
+static bool is_sgi_ppi(uint32_t id)
+{
+ /* SGIs: 0-15, PPIs: 16-31, EPPIs: 1056-1119. */
+ if (IS_SGI_PPI(id)) {
+ return true;
+ }
+
+ /* SPIs: 32-1019, ESPIs: 4096-5119. */
+ if (IS_SPI(id)) {
+ return false;
+ }
+
+ CHECK(false);
+ return false;
+}
+
+/**
+ * This function returns the id of the highest priority pending interrupt at
+ * the GIC cpu interface.
+ */
+uint32_t gicv3_get_pending_interrupt_id(void)
+{
+ return (uint32_t)read_msr(ICC_IAR1_EL1) & IAR1_EL1_INTID_MASK;
+}
+
+/**
+ * This function returns the type of the interrupt id depending on the group
+ * this interrupt has been configured under by the interrupt controller i.e.
+ * group1 Secure / group1 Non Secure. The return value can be one of the
+ * following :
+ * INTR_GROUP1S : The interrupt type is a Secure Group 1 secure interrupt.
+ * INTR_GROUP1NS: The interrupt type is a Secure Group 1 non secure
+ * interrupt.
+ */
+uint32_t gicv3_get_interrupt_type(uint32_t id, uint32_t proc_num)
+{
+ uint32_t igroup;
+ uint32_t grpmodr;
+ uintptr_t gicr_base;
+
+ /* Ensure the parameters are valid. */
+ CHECK((id < PENDING_G1S_INTID) || (id >= MIN_LPI_ID));
+ CHECK(proc_num < MAX_CPUS);
+
+ /* All LPI interrupts are Group 1 non secure. */
+ if (id >= MIN_LPI_ID) {
+ return INTR_GROUP1NS;
+ }
+
+ /* Check interrupt ID. */
+ if (is_sgi_ppi(id)) {
+ /* SGIs: 0-15, PPIs: 16-31, EPPIs: 1056-1119. */
+ gicr_base = plat_gicv3_driver.all_redist_frames[proc_num];
+ igroup = gicr_get_igroupr(gicr_base, id);
+ grpmodr = gicr_get_igrpmodr(gicr_base, id);
+ } else {
+ /* SPIs: 32-1019, ESPIs: 4096-5119. */
+ igroup = gicd_get_igroupr(plat_gicv3_driver.dist_base, id);
+ grpmodr = gicd_get_igrpmodr(plat_gicv3_driver.dist_base, id);
+ }
+
+ /*
+ * If the IGROUP bit is set, then it is a Group 1 Non secure
+ * interrupt.
+ */
+ if (igroup != 0U) {
+ return INTR_GROUP1NS;
+ }
+
+ /* If the GRPMOD bit is set, then it is a Group 1 Secure interrupt. */
+ if (grpmodr != 0U) {
+ return INTR_GROUP1S;
+ }
+
+ CHECK(false);
+}
+
+/**
+ * This function enables the interrupt identified by id. The proc_num
+ * is used if the interrupt is SGI or PPI, and programs the corresponding
+ * Redistributor interface.
+ */
+void gicv3_enable_interrupt(uint32_t id, uint32_t proc_num)
+{
+ CHECK(plat_gicv3_driver.dist_base != 0U);
+ CHECK(plat_gicv3_driver.base_redist_frame != 0U);
+ CHECK(proc_num < MAX_CPUS);
+
+ /*
+ * Ensure that any shared variable updates depending on out of band
+ * interrupt trigger are observed before enabling interrupt.
+ */
+ dsb(ish);
+
+ /* Check interrupt ID. */
+ if (is_sgi_ppi(id)) {
+ /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119. */
+ gicr_set_isenabler(
+ plat_gicv3_driver.all_redist_frames[proc_num], id);
+ } else {
+ /* For SPIs: 32-1019 and ESPIs: 4096-5119. */
+ gicd_set_isenabler(plat_gicv3_driver.dist_base, id);
+ }
+}
+
+/**
+ * This function disables the interrupt identified by id. The proc_num
+ * is used if the interrupt is SGI or PPI, and programs the corresponding
+ * Redistributor interface.
+ */
+void gicv3_disable_interrupt(uint32_t id, uint32_t proc_num)
+{
+ CHECK(plat_gicv3_driver.dist_base != 0U);
+ CHECK(plat_gicv3_driver.base_redist_frame != 0U);
+ CHECK(proc_num < MAX_CPUS);
+
+ /*
+ * Disable interrupt, and ensure that any shared variable updates
+ * depending on out of band interrupt trigger are observed afterwards.
+ */
+
+ /* Check interrupt ID. */
+ if (is_sgi_ppi(id)) {
+ /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119. */
+ gicr_set_icenabler(
+ plat_gicv3_driver.all_redist_frames[proc_num], id);
+
+ /* Write to clear enable requires waiting for pending writes. */
+ gicr_wait_for_pending_write(
+ plat_gicv3_driver.all_redist_frames[proc_num]);
+ } else {
+ /* For SPIs: 32-1019 and ESPIs: 4096-5119. */
+ gicd_set_icenabler(plat_gicv3_driver.dist_base, id);
+
+ /* Write to clear enable requires waiting for pending writes. */
+ gicd_wait_for_pending_write(plat_gicv3_driver.dist_base);
+ }
+
+ dsb(ish);
+}
+
+/**
+ * This function sets the interrupt priority as supplied for the given interrupt
+ * id.
+ */
+void gicv3_set_interrupt_priority(uint32_t id, uint32_t core_pos,
+ uint32_t priority)
+{
+ uintptr_t gicr_base;
+
+ /* Core index cannot exceed maximum core count. */
+ CHECK(core_pos < MAX_CPUS);
+
+ /* Check interrupt ID. */
+ if (is_sgi_ppi(id)) {
+ /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119. */
+ gicr_base = plat_gicv3_driver.all_redist_frames[core_pos];
+ gicr_set_ipriorityr(gicr_base, id, priority);
+ } else {
+ /* For SPIs: 32-1019 and ESPIs: 4096-5119. */
+ gicd_set_ipriorityr(plat_gicv3_driver.dist_base, id, priority);
+ }
+}
+
+/**
+ * This function assigns group for the interrupt identified by id. The proc_num
+ * is used if the interrupt is SGI or (E)PPI, and programs the corresponding
+ * Redistributor interface. The group can be any of GICV3_INTR_GROUP*.
+ */
+void gicv3_set_interrupt_type(uint32_t id, uint32_t proc_num, uint32_t type)
+{
+ bool igroup = false;
+ bool grpmod = false;
+ uintptr_t gicr_base;
+
+ CHECK(plat_gicv3_driver.dist_base != 0U);
+ CHECK(proc_num < MAX_CPUS);
+
+ switch (type) {
+ case INTR_GROUP1S:
+ igroup = false;
+ grpmod = true;
+ break;
+ case INTR_GROUP1NS:
+ igroup = true;
+ grpmod = false;
+ break;
+ default:
+ CHECK(false);
+ break;
+ }
+
+ /* Check interrupt ID. */
+ if (is_sgi_ppi(id)) {
+ /* For SGIs: 0-15, PPIs: 16-31 and EPPIs: 1056-1119. */
+ gicr_base = plat_gicv3_driver.all_redist_frames[proc_num];
+
+ igroup ? gicr_set_igroupr(gicr_base, id)
+ : gicr_clr_igroupr(gicr_base, id);
+ grpmod ? gicr_set_igrpmodr(gicr_base, id)
+ : gicr_clr_igrpmodr(gicr_base, id);
+ } else {
+ /* For SPIs: 32-1019 and ESPIs: 4096-5119. */
+
+ /* Serialize read-modify-write to Distributor registers. */
+ sl_lock(&plat_gicv3_driver.lock);
+
+ igroup ? gicd_set_igroupr(plat_gicv3_driver.dist_base, id)
+ : gicd_clr_igroupr(plat_gicv3_driver.dist_base, id);
+ grpmod ? gicd_set_igrpmodr(plat_gicv3_driver.dist_base, id)
+ : gicd_clr_igrpmodr(plat_gicv3_driver.dist_base, id);
+
+ sl_unlock(&plat_gicv3_driver.lock);
+ }
+}
+
+void gicv3_end_of_interrupt(uint32_t id)
+{
+ /*
+ * Interrupt request deassertion from peripheral to GIC happens
+ * by clearing interrupt condition by a write to the peripheral
+ * register. It is desired that the write transfer is complete
+ * before the core tries to change GIC state from 'AP/Active' to
+ * a new state on seeing 'EOI write'.
+ * Since ICC interface writes are not ordered against Device
+ * memory writes, a barrier is required to ensure the ordering.
+ * The dsb will also ensure *completion* of previous writes with
+ * DEVICE nGnRnE attribute.
+ */
+ dsb(ish);
+ write_msr(ICC_EOIR1_EL1, id);
+}
+
+uint64_t read_gicr_typer_reg(uintptr_t gicr_frame_addr)
+{
+ return io_read64(IO64_C(gicr_frame_addr + GICR_TYPER));
+}
+
+static inline uint32_t gicr_affinity_to_core_pos(uint64_t typer_reg)
+{
+ uint64_t aff3;
+ uint64_t aff2;
+ uint64_t aff1;
+ uint64_t aff0;
+ uint64_t reg;
+
+ aff3 = (typer_reg >> RDIST_AFF3_SHIFT) & (0xff);
+ aff2 = (typer_reg >> RDIST_AFF2_SHIFT) & (0xff);
+ aff1 = (typer_reg >> RDIST_AFF1_SHIFT) & (0xff);
+ aff0 = (typer_reg >> RDIST_AFF0_SHIFT) & (0xff);
+
+ /* Construct mpidr based on above affinities. */
+ reg = (aff3 << MPIDR_AFF3_SHIFT) | (aff2 << MPIDR_AFF2_SHIFT) |
+ (aff1 << MPIDR_AFF1_SHIFT) | (aff0 << MPIDR_AFF0_SHIFT);
+
+ return affinity_to_core_id(reg);
+}
+
+static inline void populate_redist_base_addrs(void)
+{
+ uintptr_t current_rdist_frame;
+ uint64_t typer_reg;
+ uint32_t core_idx;
+
+ current_rdist_frame = plat_gicv3_driver.base_redist_frame;
+
+ while (true) {
+ typer_reg = read_gicr_typer_reg(current_rdist_frame);
+ core_idx = gicr_affinity_to_core_pos(typer_reg);
+
+ CHECK(core_idx < MAX_CPUS);
+ plat_gicv3_driver.all_redist_frames[core_idx] =
+ current_rdist_frame;
+
+ /* Check if this is the last frame. */
+ if (typer_reg & REDIST_LAST_FRAME_MASK) {
+ return;
+ }
+
+ current_rdist_frame += GICV3_REDIST_FRAMES_OFFSET;
+ }
+}
+
+static uint32_t find_core_pos(void)
+{
+ uint64_t mpidr_reg;
+
+ mpidr_reg = read_msr(MPIDR_EL1);
+
+ return affinity_to_core_id(mpidr_reg);
+}
+
+/**
+ * Currently, TF-A has complete access to GIC driver and configures
+ * GIC Distributor, GIC Re-distributor and CPU interfaces as needed.
+ */
+void gicv3_distif_init(void)
+{
+ /* TODO: Currently, we skip this. */
+ return;
+
+ /* Enable G1S and G1NS interrupts. */
+ gicd_write_ctlr(
+ plat_gicv3_driver.dist_base,
+ CTLR_ENABLE_G1NS_BIT | CTLR_ENABLE_G1S_BIT | CTLR_ARE_S_BIT);
+}
+
+void gicv3_rdistif_init(uint32_t core_pos)
+{
+ /* TODO: Currently, we skip this. */
+ (void)core_pos;
+}
+
+void gicv3_cpuif_enable(uint32_t core_pos)
+{
+ /* TODO: Currently, we skip this. */
+ (void)core_pos;
+}
+
+void gicv3_send_sgi(uint32_t sgi_id, bool send_to_all, uint32_t target_list,
+ bool to_this_security_state)
+{
+ uint64_t sgir;
+ uint64_t irm;
+ uint64_t mpidr_reg;
+
+ CHECK(is_sgi_ppi(sgi_id));
+
+ mpidr_reg = read_msr(MPIDR_EL1);
+ sgir = (sgi_id & SGIR_INTID_MASK) << SGIR_INTID_SHIFT;
+
+ /* Check the interrupt routing mode. */
+ if (send_to_all) {
+ irm = 1;
+ } else {
+ irm = 0;
+
+ /*
+ * Find the affinity path of the PE for which SGI will be
+ * generated.
+ */
+
+ uint64_t aff1;
+ uint64_t aff2;
+ uint64_t aff3;
+
+ /*
+ * Target List is a one hot encoding representing which cores
+ * will be delivered the interrupt. At least one has to be
+ * enabled.
+ */
+
+ CHECK(target_list != 0U);
+
+ aff3 = (mpidr_reg >> MPIDR_AFF3_SHIFT) & (0xff);
+ aff2 = (mpidr_reg >> MPIDR_AFF2_SHIFT) & (0xff);
+ aff1 = (mpidr_reg >> MPIDR_AFF1_SHIFT) & (0xff);
+
+ /* Populate the various affinity fields. */
+ sgir |= ((aff3 & SGIR_AFF_MASK) << SGIR_AFF3_SHIFT) |
+ ((aff2 & SGIR_AFF_MASK) << SGIR_AFF2_SHIFT) |
+ ((aff1 & SGIR_AFF_MASK) << SGIR_AFF1_SHIFT);
+
+ /* Construct the SGI target affinity. */
+ sgir |= (target_list & SGIR_TGT_MASK) << SGIR_TGT_SHIFT;
+ }
+
+ /* Populate the Interrupt Routing Mode field. */
+ sgir |= (irm & SGIR_IRM_MASK) << SGIR_IRM_SHIFT;
+
+ if (to_this_security_state) {
+ write_msr(ICC_SGI1R_EL1, sgir);
+ } else {
+ write_msr(ICC_ASGI1R_EL1, sgir);
+ }
+
+ isb();
+}
+
+bool gicv3_driver_init(struct mm_stage1_locked stage1_locked,
+ struct mpool *ppool)
+{
+ void *base_addr;
+
+ base_addr = mm_identity_map(stage1_locked, pa_init(GICD_BASE),
+ pa_init(GICD_BASE + GICD_SIZE),
+ MM_MODE_R | MM_MODE_W | MM_MODE_D, ppool);
+ if (base_addr == NULL) {
+ dlog_error("Could not map GICv3 into Hafnium memory map\n");
+ return false;
+ }
+
+ plat_gicv3_driver.dist_base = (uintptr_t)base_addr;
+
+ base_addr = mm_identity_map(
+ stage1_locked, pa_init(GICR_BASE),
+ pa_init(GICR_BASE + MAX_CPUS * GICV3_REDIST_SIZE_PER_PE),
+ MM_MODE_R | MM_MODE_W | MM_MODE_D, ppool);
+
+ if (base_addr == NULL) {
+ dlog_error("Could not map GICv3 into Hafnium memory map\n");
+ return false;
+ }
+
+ plat_gicv3_driver.base_redist_frame = (uintptr_t)base_addr;
+
+ populate_redist_base_addrs();
+
+ return true;
+}
+
bool plat_interrupts_controller_driver_init(
const struct fdt *fdt, struct mm_stage1_locked stage1_locked,
struct mpool *ppool)
{
(void)fdt;
- (void)stage1_locked;
- (void)ppool;
+
+ if (!gicv3_driver_init(stage1_locked, ppool)) {
+ dlog_error("Failed to initialize GICv3 driver\n");
+ return false;
+ }
+
+ gicv3_distif_init();
+ gicv3_rdistif_init(find_core_pos());
+
return true;
}
void plat_interrupts_controller_hw_init(struct cpu *c)
{
(void)c;
+ gicv3_cpuif_enable(find_core_pos());
}
void plat_interrupts_set_priority_mask(uint8_t min_priority)
@@ -31,7 +487,100 @@
write_msr(ICC_PMR_EL1, min_priority);
}
+uint8_t plat_interrupts_get_priority_mask(void)
+{
+ return read_msr(ICC_PMR_EL1);
+}
+
+void plat_interrupts_set_priority(uint32_t id, uint32_t core_pos,
+ uint32_t priority)
+{
+ gicv3_set_interrupt_priority(id, core_pos, priority);
+}
+
+void plat_interrupts_enable(uint32_t id, uint32_t core_pos)
+{
+ gicv3_enable_interrupt(id, core_pos);
+}
+
+void plat_interrupts_disable(uint32_t id, uint32_t core_pos)
+{
+ gicv3_disable_interrupt(id, core_pos);
+}
+
+void plat_interrupts_set_type(uint32_t id, uint32_t type)
+{
+ gicv3_set_interrupt_type(id, find_core_pos(), type);
+}
+
+uint32_t plat_interrupts_get_type(uint32_t id)
+{
+ return gicv3_get_interrupt_type(id, find_core_pos());
+}
+
+uint32_t plat_interrupts_get_pending_interrupt_id(void)
+{
+ return gicv3_get_pending_interrupt_id();
+}
+
+void plat_interrupts_end_of_interrupt(uint32_t id)
+{
+ gicv3_end_of_interrupt(id);
+}
+
+/**
+ * Configure Group, priority, edge/level of the interrupt and enable it.
+ */
void plat_interrupts_configure_interrupt(struct interrupt_descriptor int_desc)
{
- (void)int_desc;
+ uint32_t core_idx = find_core_pos();
+ uint32_t level_cfg = 0U;
+ uint32_t intr_num = interrupt_desc_get_id(int_desc);
+
+ CHECK(core_idx < MAX_CPUS);
+
+ /* Configure the interrupt as either G1S or G1NS. */
+ if (interrupt_desc_get_sec_state(int_desc) != 0) {
+ gicv3_set_interrupt_type(intr_num, core_idx, INTR_GROUP1S);
+ } else {
+ gicv3_set_interrupt_type(intr_num, core_idx, INTR_GROUP1NS);
+ }
+
+ /* Program the interrupt priority. */
+ gicv3_set_interrupt_priority(intr_num, core_idx,
+ interrupt_desc_get_priority(int_desc));
+
+ if (interrupt_desc_get_config(int_desc) != 0) {
+ level_cfg = 1U;
+ }
+
+ /* Set interrupt configuration. */
+ if (is_sgi_ppi(intr_num)) {
+ /* GICR interface. */
+ gicr_set_icfgr(plat_gicv3_driver.all_redist_frames[core_idx],
+ intr_num, level_cfg);
+ } else {
+ /* GICD interface. */
+ gicd_set_icfgr(plat_gicv3_driver.dist_base, intr_num,
+ level_cfg);
+ }
+
+ /* Target SPI to primary CPU using affinity routing. */
+ if (IS_SPI(intr_num)) {
+ uint64_t gic_affinity_val;
+
+ gic_affinity_val =
+ gicd_irouter_val_from_mpidr(read_msr(MPIDR_EL1), 0U);
+ gicd_write_irouter(plat_gicv3_driver.dist_base, intr_num,
+ gic_affinity_val);
+ }
+
+ /* Enable the interrupt now. */
+ gicv3_enable_interrupt(intr_num, core_idx);
+}
+
+void plat_interrupts_send_sgi(uint32_t id, bool send_to_all,
+ uint32_t target_list, bool to_this_security_state)
+{
+ gicv3_send_sgi(id, send_to_all, target_list, to_this_security_state);
}
diff --git a/src/arch/aarch64/plat/interrupts/gicv3_helpers.h b/src/arch/aarch64/plat/interrupts/gicv3_helpers.h
new file mode 100644
index 0000000..562e3a6
--- /dev/null
+++ b/src/arch/aarch64/plat/interrupts/gicv3_helpers.h
@@ -0,0 +1,609 @@
+/*
+ * Copyright 2021 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include "hf/cpu.h"
+#include "hf/dlog.h"
+#include "hf/io.h"
+#include "hf/panic.h"
+#include "hf/plat/interrupts.h"
+#include "hf/static_assert.h"
+#include "hf/types.h"
+
+#include "msr.h"
+
+#define BIT_32(nr) (UINT32_C(1) << (nr))
+
+#define MPIDR_AFFINITY_MASK (0xff00ffffff)
+#define MPIDR_AFFLVL_MASK (0xff)
+#define MPIDR_AFF0_SHIFT (0)
+#define MPIDR_AFF1_SHIFT (8)
+#define MPIDR_AFF2_SHIFT (16)
+#define MPIDR_AFF3_SHIFT (32)
+#define RDIST_AFF3_SHIFT (56)
+#define RDIST_AFF2_SHIFT (48)
+#define RDIST_AFF1_SHIFT (40)
+#define RDIST_AFF0_SHIFT (32)
+
+/* Mask for the configuration field common to all GIC interfaces */
+#define GIC_CFG_MASK (0x3)
+
+/**
+ * Common GIC Distributor interface register offsets
+ */
+#define GICD_CTLR (0x0)
+#define GICD_TYPER (0x4)
+#define GICD_IIDR (0x8)
+#define GICD_IGROUPR (0x80)
+#define GICD_ISENABLER (0x100)
+#define GICD_ICENABLER (0x180)
+#define GICD_ISPENDR (0x200)
+#define GICD_ICPENDR (0x280)
+#define GICD_ISACTIVER (0x300)
+#define GICD_ICACTIVER (0x380)
+#define GICD_IPRIORITYR (0x400)
+#define GICD_ICFGR (0xc00)
+#define GICD_NSACR (0xe00)
+
+/* GICD_CTLR bit definitions */
+#define CTLR_ENABLE_G0_SHIFT 0
+#define CTLR_ENABLE_G0_MASK (0x1)
+#define CTLR_ENABLE_G0_BIT BIT_32(CTLR_ENABLE_G0_SHIFT)
+
+#define IGROUPR_SHIFT 5
+#define ISENABLER_SHIFT 5
+#define ICENABLER_SHIFT ISENABLER_SHIFT
+#define ISPENDR_SHIFT 5
+#define ICPENDR_SHIFT ISPENDR_SHIFT
+#define ISACTIVER_SHIFT 5
+#define ICACTIVER_SHIFT ISACTIVER_SHIFT
+#define IPRIORITYR_SHIFT 2
+#define ITARGETSR_SHIFT 2
+#define ICFGR_SHIFT 4
+#define NSACR_SHIFT 4
+
+#define GIC_PRI_MASK (0xff)
+
+/**
+ * GICv3 and 3.1 specific Distributor interface register offsets and constants
+ */
+#define GICD_TYPER2 (0x0c)
+#define GICD_STATUSR (0x10)
+#define GICD_SETSPI_NSR (0x40)
+#define GICD_CLRSPI_NSR (0x48)
+#define GICD_SETSPI_SR (0x50)
+#define GICD_CLRSPI_SR (0x58)
+#define GICD_IGRPMODR (0xd00)
+#define GICD_IGROUPRE (0x1000)
+#define GICD_ISENABLERE (0x1200)
+#define GICD_ICENABLERE (0x1400)
+#define GICD_ISPENDRE (0x1600)
+#define GICD_ICPENDRE (0x1800)
+#define GICD_ISACTIVERE (0x1a00)
+#define GICD_ICACTIVERE (0x1c00)
+#define GICD_IPRIORITYRE (0x2000)
+#define GICD_ICFGRE (0x3000)
+#define GICD_IGRPMODRE (0x3400)
+#define GICD_NSACRE (0x3600)
+
+/**
+ * GICD_IROUTER<n> register is at 0x6000 + 8n, where n is the interrupt ID
+ * and n >= 32, making the effective offset as 0x6100
+ */
+#define GICD_IROUTER (0x6000)
+#define GICD_IROUTERE (0x8000)
+
+#define GICD_PIDR2_GICV3 (0xffe8)
+
+#define IGRPMODR_SHIFT 5
+
+/* GICD_CTLR bit definitions */
+#define CTLR_ENABLE_G1NS_SHIFT 1
+#define CTLR_ENABLE_G1S_SHIFT 2
+#define CTLR_ARE_S_SHIFT 4
+#define CTLR_ARE_NS_SHIFT 5
+#define CTLR_DS_SHIFT 6
+#define CTLR_E1NWF_SHIFT 7
+#define GICD_CTLR_RWP_SHIFT 31
+
+#define CTLR_ENABLE_G1NS_MASK (0x1)
+#define CTLR_ENABLE_G1S_MASK (0x1)
+#define CTLR_ARE_S_MASK (0x1)
+#define CTLR_ARE_NS_MASK (0x1)
+#define CTLR_DS_MASK (0x1)
+#define CTLR_E1NWF_MASK (0x1)
+#define GICD_CTLR_RWP_MASK (0x1)
+
+#define CTLR_ENABLE_G1NS_BIT BIT_32(CTLR_ENABLE_G1NS_SHIFT)
+#define CTLR_ENABLE_G1S_BIT BIT_32(CTLR_ENABLE_G1S_SHIFT)
+#define CTLR_ARE_S_BIT BIT_32(CTLR_ARE_S_SHIFT)
+#define CTLR_ARE_NS_BIT BIT_32(CTLR_ARE_NS_SHIFT)
+#define CTLR_DS_BIT BIT_32(CTLR_DS_SHIFT)
+#define CTLR_E1NWF_BIT BIT_32(CTLR_E1NWF_SHIFT)
+#define GICD_CTLR_RWP_BIT BIT_32(GICD_CTLR_RWP_SHIFT)
+
+/* GICD_IROUTER shifts and masks */
+#define IROUTER_SHIFT 0
+#define IROUTER_IRM_SHIFT 31
+#define IROUTER_IRM_MASK (0x1)
+
+#define GICV3_IRM_PE (0)
+#define GICV3_IRM_ANY (1)
+
+#define NUM_OF_DIST_REGS 30
+
+/* GICD_TYPER shifts and masks */
+#define TYPER_ESPI (1 << 8)
+#define TYPER_DVIS (1 << 18)
+#define TYPER_ESPI_RANGE_MASK (0x1f)
+#define TYPER_ESPI_RANGE_SHIFT (27)
+#define TYPER_ESPI_RANGE (TYPER_ESPI_MASK << TYPER_ESPI_SHIFT)
+
+/**
+ * Common GIC Redistributor interface registers & constants
+ */
+#define GICR_SGIBASE_OFFSET (65536) /* 64 KB */
+#define GICR_CTLR (0x0)
+#define GICR_IIDR (0x04)
+#define GICR_TYPER (0x08)
+#define GICR_STATUSR (0x10)
+#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))
+
+#define GICR_IGROUPR GICR_IGROUPR0
+#define GICR_ISENABLER GICR_ISENABLER0
+#define GICR_ICENABLER GICR_ICENABLER0
+#define GICR_ISPENDR GICR_ISPENDR0
+#define GICR_ICPENDR GICR_ICPENDR0
+#define GICR_ISACTIVER GICR_ISACTIVER0
+#define GICR_ICACTIVER GICR_ICACTIVER0
+#define GICR_ICFGR GICR_ICFGR0
+#define GICR_IGRPMODR GICR_IGRPMODR0
+
+/* GICR_CTLR bit definitions */
+#define GICR_CTLR_UWP_SHIFT 31
+#define GICR_CTLR_UWP_MASK (0x1)
+#define GICR_CTLR_UWP_BIT BIT_32(GICR_CTLR_UWP_SHIFT)
+#define GICR_CTLR_RWP_SHIFT 3
+#define GICR_CTLR_RWP_MASK (0x1)
+#define GICR_CTLR_RWP_BIT BIT_32(GICR_CTLR_RWP_SHIFT)
+#define GICR_CTLR_EN_LPIS_BIT BIT_32(0)
+
+/**
+ * GICv3 and 3.1 CPU interface registers & constants
+ */
+/* ICC_SRE bit definitions */
+#define ICC_SRE_EN_BIT BIT_32(3)
+#define ICC_SRE_DIB_BIT BIT_32(2)
+#define ICC_SRE_DFB_BIT BIT_32(1)
+#define ICC_SRE_SRE_BIT BIT_32(0)
+
+/* ICC_IGRPEN1_EL3 bit definitions */
+#define IGRPEN1_EL3_ENABLE_G1NS_SHIFT 0
+#define IGRPEN1_EL3_ENABLE_G1S_SHIFT 1
+
+#define IGRPEN1_EL3_ENABLE_G1NS_BIT BIT_32(IGRPEN1_EL3_ENABLE_G1NS_SHIFT)
+#define IGRPEN1_EL3_ENABLE_G1S_BIT BIT_32(IGRPEN1_EL3_ENABLE_G1S_SHIFT)
+
+/* ICC_IGRPEN0_EL1 bit definitions */
+#define IGRPEN1_EL1_ENABLE_G0_SHIFT 0
+#define IGRPEN1_EL1_ENABLE_G0_BIT BIT_32(IGRPEN1_EL1_ENABLE_G0_SHIFT)
+
+/* ICC_HPPIR0_EL1 bit definitions */
+#define HPPIR0_EL1_INTID_SHIFT 0
+#define HPPIR0_EL1_INTID_MASK (0xffffff)
+
+/* ICC_HPPIR1_EL1 bit definitions */
+#define HPPIR1_EL1_INTID_SHIFT 0
+#define HPPIR1_EL1_INTID_MASK (0xffffff)
+
+/* ICC_IAR0_EL1 bit definitions */
+#define IAR0_EL1_INTID_SHIFT 0
+#define IAR0_EL1_INTID_MASK (0xffffff)
+
+/* ICC_IAR1_EL1 bit definitions */
+#define IAR1_EL1_INTID_SHIFT 0
+#define IAR1_EL1_INTID_MASK (0xffffff)
+
+/* ICC SGI macros */
+#define SGIR_TGT_SHIFT 0
+#define SGIR_TGT_MASK 0xffff
+#define SGIR_AFF1_SHIFT 16
+#define SGIR_INTID_SHIFT 24
+#define SGIR_INTID_MASK 0xf
+#define SGIR_AFF2_SHIFT 32
+#define SGIR_IRM_SHIFT 40
+#define SGIR_IRM_MASK 0x1
+#define SGIR_AFF3_SHIFT 48
+#define SGIR_AFF_MASK 0xf
+
+#define SGIR_IRM_TO_AFF (0)
+
+/**
+ * GICv3 and 3.1 miscellaneous definitions
+ */
+/* Interrupt group definitions */
+#define INTR_GROUP1S (0)
+#define INTR_GROUP0 (1)
+#define INTR_GROUP1NS (2)
+
+/* Interrupt IDs reported by the HPPIR and IAR registers */
+#define PENDING_G1S_INTID (1020)
+#define PENDING_G1NS_INTID (8192)
+
+/* Constant to categorize LPI interrupt */
+#define MIN_LPI_ID (8192)
+
+/* GICv3 can only target up to 16 PEs with SGI */
+#define GICV3_MAX_SGI_TARGETS (16)
+
+/* PPIs INTIDs 16-31 */
+#define MAX_PPI_ID (31)
+#define MIN_SPI_ID (32)
+#define MAX_SPI_ID (1019)
+
+/* SGIs: 0-15, PPIs: 16-31 */
+#define IS_SGI_PPI(id) ((id) <= MAX_PPI_ID)
+
+/* SPIs: 32-1019 */
+#define IS_SPI(id) (((id) >= MIN_SPI_ID) && ((id) <= MAX_SPI_ID))
+
+/**
+ * GICv3 private macro definitions
+ */
+
+/* Constants to indicate the status of the RWP bit */
+#define RWP_TRUE (1)
+#define RWP_FALSE (0)
+
+/* Calculate GIC register bit number corresponding to its interrupt ID */
+#define BIT_NUM(REG, id) ((id) & ((1U << REG##R_SHIFT) - 1U))
+
+/*
+ * Calculate 8, 32 and 64-bit GICD register offset
+ * corresponding to its interrupt ID
+ */
+#define GICD_OFFSET_8(REG, id) (GICD_##REG##R + (uintptr_t)(id))
+
+#define GICD_OFFSET(REG, id) \
+ (GICD_##REG##R + (((uintptr_t)(id) >> REG##R_SHIFT) << 2))
+
+#define GICD_OFFSET_64(REG, id) \
+ (GICD_##REG##R + (((uintptr_t)(id) >> REG##R_SHIFT) << 3))
+
+/*
+ * Read/Write 8, 32 and 64-bit GIC Distributor register
+ * corresponding to its interrupt ID
+ */
+#define GICD_READ(REG, base, id) \
+ io_read32(IO32_C((base) + GICD_OFFSET(REG, (id))))
+
+#define GICD_READ_64(REG, base, id) \
+ io_read64(IO64_C((base) + GICD_OFFSET_64(REG, (id))))
+
+#define GICD_WRITE_8(REG, base, id, val) \
+ io_write8(IO8_C((base) + GICD_OFFSET_8(REG, (id))), (val))
+
+#define GICD_WRITE(REG, base, id, val) \
+ io_write32(IO32_C((base) + GICD_OFFSET(REG, (id)), (val)))
+
+#define GICD_WRITE_64(REG, base, id, val) \
+ io_write64(IO64_C((base) + GICD_OFFSET_64(REG, (id))), (val))
+
+/*
+ * Bit operations on GIC Distributor register corresponding
+ * to its interrupt ID
+ */
+/* Get bit in GIC Distributor register */
+#define GICD_GET_BIT(REG, base, id) \
+ ((io_read32(IO32_C((base) + GICD_OFFSET(REG, (id)))) >> \
+ BIT_NUM(REG, (id))) & \
+ 1U)
+
+/* Set bit in GIC Distributor register */
+#define GICD_SET_BIT(REG, base, id) \
+ io_setbits32(IO32_C((base) + GICD_OFFSET(REG, (id))), \
+ ((uint32_t)1 << BIT_NUM(REG, (id))))
+
+/* Clear bit in GIC Distributor register */
+#define GICD_CLR_BIT(REG, base, id) \
+ io_clrbits32(IO32_C((base) + GICD_OFFSET(REG, (id))), \
+ ((uint32_t)1 << BIT_NUM(REG, (id))))
+
+/* Write bit in GIC Distributor register */
+#define GICD_WRITE_BIT(REG, base, id) \
+ io_write32(IO32_C((base) + GICD_OFFSET(REG, (id))), \
+ ((uint32_t)1 << BIT_NUM(REG, (id))))
+
+/**
+ * Calculate 8 and 32-bit GICR register offset
+ * corresponding to its interrupt ID
+ */
+#define GICR_OFFSET_8(REG, id) (GICR_##REG##R + (uintptr_t)(id))
+
+#define GICR_OFFSET(REG, id) \
+ (GICR_##REG##R + (((uintptr_t)(id) >> REG##R_SHIFT) << 2))
+
+/* Read/Write GIC Redistributor register corresponding to its interrupt ID */
+#define GICR_READ(REG, base, id) \
+ io_read32(IO32_C((base) + GICR_OFFSET(REG, (id))))
+
+#define GICR_WRITE_8(REG, base, id, val) \
+ io_write8(IO8_C((base) + GICR_OFFSET_8(REG, (id))), (val))
+
+#define GICR_WRITE(REG, base, id, val) \
+ io_write32(IO32_C((base) + GICR_OFFSET(REG, (id))), (val))
+
+/*
+ * Bit operations on GIC Redistributor register
+ * corresponding to its interrupt ID
+ */
+
+/* Get bit in GIC Redistributor register */
+#define GICR_GET_BIT(REG, base, id) \
+ ((io_read32(IO32_C((base) + GICR_OFFSET(REG, (id)))) >> \
+ BIT_NUM(REG, (id))) & \
+ 1U)
+
+/* Write bit in GIC Redistributor register */
+#define GICR_WRITE_BIT(REG, base, id) \
+ io_write32(IO32_C((base) + GICR_OFFSET(REG, (id))), \
+ ((uint32_t)1 << BIT_NUM(REG, (id))))
+
+/* Set bit in GIC Redistributor register */
+#define GICR_SET_BIT(REG, base, id) \
+ io_setbits32(IO32_C((base) + GICR_OFFSET(REG, (id))), \
+ ((uint32_t)1 << BIT_NUM(REG, (id))))
+
+/* Clear bit in GIC Redistributor register */
+#define GICR_CLR_BIT(REG, base, id) \
+ io_clrbits32(IO32_C((base) + GICR_OFFSET(REG, (id))), \
+ ((uint32_t)1 << BIT_NUM(REG, (id))))
+
+static inline uint64_t gicd_irouter_val_from_mpidr(uint64_t mpidr,
+ unsigned int irm)
+{
+ return (mpidr & ~(UINT32_C(0xff) << 24)) |
+ ((irm & IROUTER_IRM_MASK) << IROUTER_IRM_SHIFT);
+}
+
+/**
+ * GIC Distributor interface register accessors that are common to GICv3 & GICv2
+ */
+static inline unsigned int gicd_read_ctlr(uintptr_t base)
+{
+ return io_read32(IO32_C(base + GICD_CTLR));
+}
+
+static inline void gicd_write_ctlr(uintptr_t base, unsigned int val)
+{
+ io_write32(IO32_C(base + GICD_CTLR), val);
+}
+
+/**
+ * GIC Distributor interface accessors
+ */
+/*
+ * Wait for updates to:
+ * GICD_CTLR[2:0] - the Group Enables
+ * GICD_CTLR[7:4] - the ARE bits, E1NWF bit and DS bit
+ * GICD_ICENABLER<n> - 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) != 0U) {
+ }
+}
+
+static inline void gicd_write_irouter(uintptr_t base, unsigned int id,
+ uint64_t affinity)
+{
+ CHECK(id >= MIN_SPI_ID);
+ GICD_WRITE_64(IROUTE, base, id, affinity);
+}
+
+/**
+ * GIC Redistributor interface accessors
+ */
+static inline uint32_t gicr_read_ctlr(uintptr_t base)
+{
+ return io_read32(IO32_C(base + GICR_CTLR));
+}
+
+/*
+ * Wait for updates to:
+ * GICR_ICENABLER0
+ * GICR_CTLR.DPG1S
+ * GICR_CTLR.DPG1NS
+ * GICR_CTLR.DPG0
+ * GICR_CTLR, which clears EnableLPIs from 1 to 0
+ */
+static inline void gicr_wait_for_pending_write(uintptr_t gicr_base)
+{
+ while ((gicr_read_ctlr(gicr_base) & GICR_CTLR_RWP_BIT) != 0U) {
+ }
+}
+
+/**
+ * GIC Distributor functions for accessing the GIC registers
+ * corresponding to a single interrupt ID. These functions use bitwise
+ * operations or appropriate register accesses to modify or return
+ * the bit-field corresponding the single interrupt ID.
+ */
+
+/**
+ * Accessors to set the bits corresponding to interrupt ID
+ * in GIC Distributor ICFGR and ICFGRE.
+ */
+void gicd_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg)
+{
+ /* Interrupt configuration is a 2-bit field */
+ unsigned int bit_shift = BIT_NUM(ICFG, id) << 1U;
+
+ /* Clear the field, and insert required configuration */
+ io_clrsetbits32(IO32_C(base + GICD_OFFSET(ICFG, id)),
+ (uint32_t)GIC_CFG_MASK << bit_shift,
+ (cfg & GIC_CFG_MASK) << bit_shift);
+}
+
+/**
+ * Accessors to get/set/clear the bit corresponding to interrupt ID
+ * in GIC Distributor IGROUPR and IGROUPRE.
+ */
+unsigned int gicd_get_igroupr(uintptr_t base, unsigned int id)
+{
+ return GICD_GET_BIT(IGROUP, base, id);
+}
+
+void gicd_set_igroupr(uintptr_t base, unsigned int id)
+{
+ GICD_SET_BIT(IGROUP, base, id);
+}
+
+void gicd_clr_igroupr(uintptr_t base, unsigned int id)
+{
+ GICD_CLR_BIT(IGROUP, base, id);
+}
+
+/**
+ * Accessors to get/set/clear the bit corresponding to interrupt ID
+ * in GIC Distributor IGRPMODR and IGRPMODRE.
+ */
+unsigned int gicd_get_igrpmodr(uintptr_t base, unsigned int id)
+{
+ return GICD_GET_BIT(IGRPMOD, base, id);
+}
+
+void gicd_set_igrpmodr(uintptr_t base, unsigned int id)
+{
+ GICD_SET_BIT(IGRPMOD, base, id);
+}
+
+void gicd_clr_igrpmodr(uintptr_t base, unsigned int id)
+{
+ GICD_CLR_BIT(IGRPMOD, base, id);
+}
+
+/**
+ * Accessors to set the bit corresponding to interrupt ID
+ * in GIC Distributor ICENABLER and ICENABLERE.
+ */
+void gicd_set_icenabler(uintptr_t base, unsigned int id)
+{
+ GICD_WRITE_BIT(ICENABLE, base, id);
+}
+
+/**
+ * Accessors to set the bit corresponding to interrupt ID
+ * in GIC Distributor ISENABLER and ISENABLERE.
+ */
+void gicd_set_isenabler(uintptr_t base, unsigned int id)
+{
+ GICD_WRITE_BIT(ISENABLE, base, id);
+}
+
+/**
+ * Accessors to set the bit corresponding to interrupt ID
+ * in GIC Distributor IPRIORITYR and IPRIORITYRE.
+ */
+void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri)
+{
+ GICD_WRITE_8(IPRIORITY, base, id, (uint8_t)(pri & GIC_PRI_MASK));
+}
+
+/**
+ * Accessor to set the byte corresponding to interrupt `id`
+ * in GIC Redistributor IPRIORITYR and IPRIORITYRE.
+ */
+void gicr_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri)
+{
+ GICR_WRITE_8(IPRIORITY, base, id, (uint8_t)(pri & GIC_PRI_MASK));
+}
+
+/**
+ * Accessors to get/set/clear the bit corresponding to interrupt `id`
+ * from GIC Redistributor IGROUPR0 and IGROUPRE
+ */
+unsigned int gicr_get_igroupr(uintptr_t base, unsigned int id)
+{
+ return GICR_GET_BIT(IGROUP, base, id);
+}
+
+void gicr_set_igroupr(uintptr_t base, unsigned int id)
+{
+ GICR_SET_BIT(IGROUP, base, id);
+}
+
+void gicr_clr_igroupr(uintptr_t base, unsigned int id)
+{
+ GICR_CLR_BIT(IGROUP, base, id);
+}
+
+/**
+ * Accessors to get/set/clear the bit corresponding to interrupt `id`
+ * from GIC Redistributor IGRPMODR0 and IGRPMODRE
+ */
+unsigned int gicr_get_igrpmodr(uintptr_t base, unsigned int id)
+{
+ return GICR_GET_BIT(IGRPMOD, base, id);
+}
+
+void gicr_set_igrpmodr(uintptr_t base, unsigned int id)
+{
+ GICR_SET_BIT(IGRPMOD, base, id);
+}
+
+void gicr_clr_igrpmodr(uintptr_t base, unsigned int id)
+{
+ GICR_CLR_BIT(IGRPMOD, base, id);
+}
+
+/**
+ * Accessor to write the bit corresponding to interrupt `id`
+ * in GIC Redistributor ISENABLER0 and ISENABLERE
+ */
+void gicr_set_isenabler(uintptr_t base, unsigned int id)
+{
+ GICR_WRITE_BIT(ISENABLE, base, id);
+}
+
+/**
+ * Accessor to write the bit corresponding to interrupt `id`
+ * in GIC Redistributor ICENABLER0 and ICENABLERE
+ */
+void gicr_set_icenabler(uintptr_t base, unsigned int id)
+{
+ GICR_WRITE_BIT(ICENABLE, base, id);
+}
+
+/**
+ * Accessor to set the bit fields corresponding to interrupt `id`
+ * in GIC Redistributor ICFGR0, ICFGR1 and ICFGRE
+ */
+void gicr_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg)
+{
+ /* Interrupt configuration is a 2-bit field */
+ unsigned int bit_shift = BIT_NUM(ICFG, id) << 1U;
+
+ /* Clear the field, and insert required configuration */
+ io_clrsetbits32(IO32_C(base + GICR_OFFSET(ICFG, id)),
+ (uint32_t)GIC_CFG_MASK << bit_shift,
+ (cfg & GIC_CFG_MASK) << bit_shift);
+}