Merge changes from topic "bk/gicv5_full"

* changes:
  feat(realm): set the PMU IRQ number depending on GIC version
  feat(fvp): set the timer IRQ number depending on GIC version
  refactor(gic): defer IRQ handler management to the GIC driver
  refactor(gic): make the concept of SGI generic
  feat(gicv5): add a GICv5 driver
  feat(gicv5): add GICv5 instructions and register accessors
diff --git a/drivers/arm/gic/aarch64/gic_v5.c b/drivers/arm/gic/aarch64/gic_v5.c
new file mode 100644
index 0000000..74fc590
--- /dev/null
+++ b/drivers/arm/gic/aarch64/gic_v5.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <arch.h>
+#include <arch_features.h>
+#include <arch_helpers.h>
+#include <drivers/arm/gic_v5.h>
+#include <platform.h>
+
+#include <platform_def.h>
+
+/*
+ * Data structure to store the GIC per CPU context before entering
+ * a suspend to powerdown (with loss of context).
+ */
+struct gicv5_pcpu_ctx {
+	/* CPU IF registers. Only those currently in use */
+	u_register_t icc_cr0_el1;
+	/* PPI registers. We don't touch handling mode. */
+	u_register_t icc_ppi_enabler[2];
+	u_register_t icc_ppi_xpendr[2];
+	u_register_t icc_ppi_priorityr[16];
+};
+
+static uintptr_t irs_base;
+static struct gicv5_pcpu_ctx cpu_ctx[PLATFORM_CORE_COUNT];
+
+/* CPU MPIDR != GICv5 IAFFID. This holds the mapping, initialised on CPU_ON. */
+static uint16_t iaffids[PLATFORM_CORE_COUNT];
+
+/* the IST is a power of 2 since that's what goes in IRS_IST_CFGR.LPI_ID_BITS */
+struct l2_iste ist[next_power_of_2(PLATFORM_CORE_COUNT) * IRQ_NUM_SGIS];
+
+static ppi_desc ppi_desc_table[PLATFORM_CORE_COUNT][GICV5_MAX_PPI_ID];
+static spi_desc spi_desc_table[PLAT_MAX_SPI_OFFSET_ID];
+static spi_desc lpi_desc_table[PLATFORM_CORE_COUNT * IRQ_NUM_SGIS];
+
+static inline uint8_t log2(uint32_t num)
+{
+	return (31 - __builtin_clz(num));
+}
+
+static inline bool is_interrupt(unsigned int interrupt_id)
+{
+	unsigned int_type = EXTRACT(INT_TYPE, interrupt_id);
+
+	return int_type == INT_PPI || int_type == INT_LPI || int_type == INT_SPI;
+}
+
+static inline u_register_t read_icc_ppi_priorityrn(unsigned n)
+{
+	switch (n) {
+	case 0:
+		return read_icc_ppi_priorityr0();
+	case 1:
+		return read_icc_ppi_priorityr1();
+	case 2:
+		return read_icc_ppi_priorityr2();
+	case 3:
+		return read_icc_ppi_priorityr3();
+	case 4:
+		return read_icc_ppi_priorityr4();
+	case 5:
+		return read_icc_ppi_priorityr5();
+	case 6:
+		return read_icc_ppi_priorityr6();
+	case 7:
+		return read_icc_ppi_priorityr7();
+	case 8:
+		return read_icc_ppi_priorityr8();
+	case 9:
+		return read_icc_ppi_priorityr9();
+	case 10:
+		return read_icc_ppi_priorityr10();
+	case 11:
+		return read_icc_ppi_priorityr11();
+	case 12:
+		return read_icc_ppi_priorityr12();
+	case 13:
+		return read_icc_ppi_priorityr13();
+	case 14:
+		return read_icc_ppi_priorityr14();
+	case 15:
+		return read_icc_ppi_priorityr15();
+	default:
+		panic();
+	}
+}
+
+static inline void write_icc_ppi_priorityrn(unsigned n, u_register_t val)
+{
+	switch (n) {
+	case 0:
+		return write_icc_ppi_priorityr0(val);
+	case 1:
+		return write_icc_ppi_priorityr1(val);
+	case 2:
+		return write_icc_ppi_priorityr2(val);
+	case 3:
+		return write_icc_ppi_priorityr3(val);
+	case 4:
+		return write_icc_ppi_priorityr4(val);
+	case 5:
+		return write_icc_ppi_priorityr5(val);
+	case 6:
+		return write_icc_ppi_priorityr6(val);
+	case 7:
+		return write_icc_ppi_priorityr7(val);
+	case 8:
+		return write_icc_ppi_priorityr8(val);
+	case 9:
+		return write_icc_ppi_priorityr9(val);
+	case 10:
+		return write_icc_ppi_priorityr10(val);
+	case 11:
+		return write_icc_ppi_priorityr11(val);
+	case 12:
+		return write_icc_ppi_priorityr12(val);
+	case 13:
+		return write_icc_ppi_priorityr13(val);
+	case 14:
+		return write_icc_ppi_priorityr14(val);
+	case 15:
+		return write_icc_ppi_priorityr15(val);
+	default:
+		panic();
+	}
+}
+
+bool is_gicv5_mode(void)
+{
+	return is_feat_gcie_supported();
+}
+
+bool gicv5_is_irq_spi(unsigned int irq_num)
+{
+	return EXTRACT(INT_TYPE, irq_num) == INT_SPI;
+}
+
+void gicv5_enable_cpuif(void)
+{
+	write_icc_cr0_el1(read_icc_cr0_el1() | ICC_CR0_EL1_EN_BIT);
+	/* make sure effects are visible */
+	isb();
+}
+
+void gicv5_setup_cpuif(void)
+{
+	iaffids[platform_get_core_pos(read_mpidr_el1())] = read_icc_iaffidr_el1();
+
+	write_icc_pcr_el1(GICV5_IDLE_PRIORITY);
+
+	gicv5_enable_cpuif();
+}
+
+void gicv5_disable_cpuif(void)
+{
+	write_icc_cr0_el1(read_icc_cr0_el1() & ~ICC_CR0_EL1_EN_BIT);
+	/* make sure effects are visible */
+	isb();
+}
+
+void gicv5_save_cpuif_context(void)
+{
+	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+
+	cpu_ctx[core_pos].icc_cr0_el1 = read_icc_cr0_el1();
+	cpu_ctx[core_pos].icc_ppi_enabler[0] = read_icc_ppi_enabler0();
+	cpu_ctx[core_pos].icc_ppi_enabler[1] = read_icc_ppi_enabler1();
+
+	cpu_ctx[core_pos].icc_ppi_xpendr[0] = read_icc_ppi_spendr0();
+	cpu_ctx[core_pos].icc_ppi_xpendr[1] = read_icc_ppi_spendr1();
+
+	for (int i = 0; i < 15; i++) {
+		cpu_ctx[core_pos].icc_ppi_priorityr[i] = read_icc_ppi_priorityrn(i);
+	}
+
+	/* Make sure all PPIs are inactive, i.e. not suspending mid interrupt */
+	assert(read_icc_ppi_sactiver0() == 0UL && read_icc_ppi_sactiver1() == 0UL);
+}
+
+void gicv5_restore_cpuif_context(void)
+{
+	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+
+	write_icc_ppi_enabler0(cpu_ctx[core_pos].icc_ppi_enabler[0]);
+	write_icc_ppi_enabler1(cpu_ctx[core_pos].icc_ppi_enabler[1]);
+
+	write_icc_ppi_spendr0(cpu_ctx[core_pos].icc_ppi_xpendr[0]);
+	write_icc_ppi_spendr1(cpu_ctx[core_pos].icc_ppi_xpendr[1]);
+	/* clear interrupts that shouldn't be pending */
+	write_icc_ppi_cpendr0(~cpu_ctx[core_pos].icc_ppi_xpendr[0]);
+	write_icc_ppi_cpendr1(~cpu_ctx[core_pos].icc_ppi_xpendr[1]);
+
+	for (int i = 0; i < 15; i++) {
+		write_icc_ppi_priorityrn(i, cpu_ctx[core_pos].icc_ppi_priorityr[i]);
+	}
+
+	/* don't bother saving it, just put the same value in */
+	write_icc_pcr_el1(GICV5_IDLE_PRIORITY);
+
+	write_icc_cr0_el1(cpu_ctx[core_pos].icc_cr0_el1);
+	/* make sure effects are visible */
+	isb();
+}
+
+void gicv5_set_priority(unsigned int interrupt_id, unsigned int priority)
+{
+	unsigned irq_idx, irq_reg;
+	u_register_t priorityr;
+
+	assert(priority < (1UL << GICCDPRI_PRIORITY_WIDTH));
+	assert(is_interrupt(interrupt_id));
+
+	if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
+		giccdpri(interrupt_id | INPLACE(GICCDPRI_PRIORITY, priority));
+		return;
+	}
+
+	/* it's a PPI, get rid of the INTR TYPE field */
+	interrupt_id = EXTRACT(INT_ID, interrupt_id);
+	irq_reg = interrupt_id / ICC_PPI_PRIORITYR_FIELD_NUM;
+	irq_idx = interrupt_id % ICC_PPI_PRIORITYR_FIELD_NUM;
+
+	priorityr = read_icc_ppi_priorityrn(irq_reg) &
+		    ICC_PPI_PRIORITYR_FIELD_MASK <<
+		    (irq_idx * ICC_PPI_PRIORITYR_FIELD_NUM);
+	write_icc_ppi_priorityrn(irq_reg, priorityr |
+			(priority << (irq_idx * ICC_PPI_PRIORITYR_FIELD_NUM)));
+}
+
+void gicv5_send_sgi(unsigned int sgi_id, unsigned int core_pos)
+{
+	giccdpend(gicv5_get_sgi_num(sgi_id, core_pos) | GICCDPEND_PENDING_BIT);
+}
+
+void gicv5_set_intr_route(unsigned int interrupt_id, unsigned int core_pos)
+{
+	assert(is_interrupt(interrupt_id));
+
+	/* PPIs are local to the CPU, can't be rerouted */
+	if (EXTRACT(INT_TYPE, interrupt_id) == INT_PPI) {
+		return;
+	}
+
+	/*
+	 * The expecation is that a core will be up (CPU_ON) before it gets
+	 * targetted by interrupts. Otherwise the IAFFID isn't available yet
+	 * and the interrupt will be misrouted.
+	 */
+	assert(iaffids[core_pos] != 0 || core_pos == 0);
+	giccdaff(INPLACE(GICCDAFF_IAFFID, iaffids[core_pos]) | interrupt_id);
+
+	/* wait for the target to take effect so retargetting an already
+	 * enabled interrupt ends up in the correct destination */
+	gsbsys();
+}
+
+void gicv5_intr_enable(unsigned int interrupt_id)
+{
+	unsigned int irq_idx;
+
+	assert(is_interrupt(interrupt_id));
+
+	if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
+		giccden(interrupt_id);
+		return;
+	}
+
+	/* it's a PPI, get rid of the INTR TYPE field */
+	interrupt_id = EXTRACT(INT_ID, interrupt_id);
+	irq_idx = interrupt_id % ICC_PPI_ENABLER_FIELD_NUM;
+
+	if (interrupt_id / ICC_PPI_ENABLER_FIELD_NUM == 0) {
+		write_icc_ppi_enabler0(read_icc_ppi_enabler0() | (1UL << irq_idx));
+	} else {
+		write_icc_ppi_enabler1(read_icc_ppi_enabler1() | (1UL << irq_idx));
+	}
+}
+
+void gicv5_intr_disable(unsigned int interrupt_id)
+{
+	unsigned int irq_idx;
+
+	assert(is_interrupt(interrupt_id));
+
+	if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
+		giccddis(interrupt_id);
+		/* wait for the interrupt to become disabled */
+		gsbsys();
+		return;
+	}
+
+	/* it's a PPI, get rid of the INTR TYPE field */
+	interrupt_id = EXTRACT(INT_ID, interrupt_id);
+	irq_idx = interrupt_id % ICC_PPI_ENABLER_FIELD_NUM;
+
+	if (interrupt_id / ICC_PPI_ENABLER_FIELD_NUM == 0) {
+		write_icc_ppi_enabler0(read_icc_ppi_enabler0() & ~(1UL << irq_idx));
+	} else {
+		write_icc_ppi_enabler1(read_icc_ppi_enabler1() & ~(1UL << irq_idx));
+	}
+}
+
+unsigned int gicv5_acknowledge_interrupt(void)
+{
+	u_register_t iar = gicrcdia();
+	assert((iar & GICRCDIA_VALID_BIT) != 0);
+
+	/* wait for the intr ack to complete (i.e. make it Active) and refetch
+	 * instructions so they don't operate on anything stale */
+	gsback();
+	isb();
+
+	return iar & ~GICRCDIA_VALID_BIT;
+}
+
+unsigned int gicv5_is_intr_pending(unsigned int interrupt_id)
+{
+	u_register_t icsr;
+	u_register_t ppi_spendr;
+
+	assert(is_interrupt(interrupt_id));
+
+	if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
+		/* request interrupt information */
+		giccdrcfg(interrupt_id);
+		/* wait for the register to update */
+		isb();
+		icsr = read_icc_icsr_el1();
+
+		/* interrupt is unreachable, something has gone wrong */
+		assert((icsr & ICC_ICSR_EL1_F_BIT) == 0);
+		return !!(icsr & ICC_ICSR_EL1_PENDING_BIT);
+	}
+
+	/* it's a PPI, get rid of the INTR TYPE field */
+	interrupt_id = EXTRACT(INT_ID, interrupt_id);
+
+	if (interrupt_id / ICC_PPI_XPENDR_FIELD_NUM == 0) {
+		ppi_spendr = read_icc_ppi_spendr0();
+	} else {
+		ppi_spendr = read_icc_ppi_spendr1();
+	}
+
+	return !!(ppi_spendr & BIT(interrupt_id % ICC_PPI_XPENDR_FIELD_NUM));
+}
+
+void gicv5_intr_clear(unsigned int interrupt_id)
+{
+	unsigned int irq_idx;
+
+	assert(is_interrupt(interrupt_id));
+
+	if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
+		giccdpend(interrupt_id);
+		return;
+	}
+
+	/* it's a PPI, get rid of the INTR TYPE field */
+	interrupt_id = EXTRACT(INT_ID, interrupt_id);
+	irq_idx = interrupt_id % ICC_PPI_XPENDR_FIELD_NUM;
+
+	if (interrupt_id / ICC_PPI_XPENDR_FIELD_NUM == 0) {
+		write_icc_ppi_cpendr0(read_icc_ppi_cpendr0() | (1UL << irq_idx));
+	} else {
+		write_icc_ppi_cpendr1(read_icc_ppi_cpendr1() | (1UL << irq_idx));
+	}
+}
+
+void gicv5_end_of_interrupt(unsigned int raw_iar)
+{
+	giccddi(raw_iar);
+	giccdeoi();
+	/* no isb as we won't interact with the gic before the eret */
+}
+
+/* currently a single IRS is expected and an ITS/IWB are not used */
+void gicv5_setup(void)
+{
+#if ENABLE_ASSERTIONS
+	uint32_t irs_idr2 = read_IRS_IDR2(irs_base);
+#endif
+	uint8_t id_bits = log2(ARRAY_SIZE(ist));
+	/* min_id_bits <= log2(length(ist)) <= id_bits */
+	assert(EXTRACT(IRS_IDR2_MIN_LPI_ID_BITS, irs_idr2) <= id_bits);
+	assert(EXTRACT(IRS_IDR2_ID_BITS, irs_idr2) >= id_bits);
+
+	/* make sure all ISTEs aren't enabled */
+	memset(ist, 0x0, sizeof(ist));
+
+	/* write zeroes throughout except LPI_ID_Bits which is the lowest 5 bits */
+	write_IRS_IST_CFGR(irs_base, id_bits);
+	/* make the IST valid */
+	write_IRS_IST_BASER(irs_base,
+			    (((uintptr_t) &ist) & MASK(IRS_IST_BASER_ADDR)) |
+			    IRS_IST_BASER_VALID_BIT);
+	WAIT_FOR_IDLE(irs_base, IRS_IST_STATUSR);
+
+	/* enable the IRS */
+	write_IRS_CR0(irs_base, IRS_CR0_IRSEN_BIT);
+	WAIT_FOR_IDLE(irs_base, IRS_CR0);
+}
+
+uint32_t gicv5_get_sgi_num(uint32_t index, unsigned int core_pos)
+{
+	assert(index <= IRQ_NUM_SGIS);
+
+	return (core_pos * IRQ_NUM_SGIS + index) | INPLACE(INT_TYPE, INT_LPI);
+}
+
+irq_handler_t *gicv5_get_irq_handler(unsigned int irq_num)
+{
+	unsigned int linear_id;
+
+	if (EXTRACT(INT_TYPE, irq_num) == INT_PPI) {
+		linear_id = platform_get_core_pos(read_mpidr_el1());
+		return &ppi_desc_table[linear_id][EXTRACT(INT_ID, irq_num)].handler;
+	}
+
+	if (EXTRACT(INT_TYPE, irq_num) == INT_LPI) {
+		return &lpi_desc_table[EXTRACT(INT_ID, irq_num)].handler;
+	}
+
+	if (EXTRACT(INT_TYPE, irq_num) == INT_SPI) {
+		return &spi_desc_table[EXTRACT(INT_ID, irq_num)].handler;
+	}
+
+	/* Interrupt should have been handled */
+	panic();
+	return NULL;
+}
+
+void gicv5_init(uintptr_t irs_base_addr)
+{
+	irs_base = irs_base_addr;
+	memset(ppi_desc_table, 0, sizeof(ppi_desc_table));
+	memset(spi_desc_table, 0, sizeof(spi_desc_table));
+	memset(lpi_desc_table, 0, sizeof(lpi_desc_table));
+}
diff --git a/drivers/arm/gic/arm_gic.c b/drivers/arm/gic/arm_gic.c
index d3b2536..2f05a48 100644
--- a/drivers/arm/gic/arm_gic.c
+++ b/drivers/arm/gic/arm_gic.c
@@ -4,6 +4,8 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <stdbool.h>
+
 #include <arch.h>
 #include <arch_helpers.h>
 #include <assert.h>
@@ -11,15 +13,32 @@
 #include <drivers/arm/gic_v2v3_common.h>
 #include <drivers/arm/gic_v2.h>
 #include <drivers/arm/gic_v3.h>
-#include <stdbool.h>
+#include <drivers/arm/gic_v5.h>
 
 /* Record whether a GICv3 was detected on the system */
 static unsigned int gicv3_detected;
+static unsigned int gicv5_detected;
+static bool gic_done_init;
+
+int arm_gic_get_version(void)
+{
+	assert(gic_done_init);
+
+	if (gicv3_detected) {
+		return 3;
+	} else if (gicv5_detected) {
+		return 5;
+	} else {
+		return 2;
+	}
+}
 
 void arm_gic_enable_interrupts_local(void)
 {
 	if (gicv3_detected)
 		gicv3_enable_cpuif();
+	else if (gicv5_detected)
+		gicv5_enable_cpuif();
 	else
 		gicv2_enable_cpuif();
 }
@@ -29,6 +48,8 @@
 	if (gicv3_detected) {
 		gicv3_probe_redistif_addr();
 		gicv3_setup_cpuif();
+	} else if (gicv5_detected) {
+		gicv5_setup_cpuif();
 	} else {
 		gicv2_probe_gic_cpu_id();
 		gicv2_setup_cpuif();
@@ -39,6 +60,8 @@
 {
 	if (gicv3_detected)
 		gicv3_disable_cpuif();
+	else if (gicv5_detected)
+		gicv5_disable_cpuif();
 	else
 		gicv2_disable_cpuif();
 }
@@ -47,6 +70,8 @@
 {
 	if (gicv3_detected)
 		gicv3_save_cpuif_context();
+	else if (gicv5_detected)
+		gicv5_save_cpuif_context();
 	else
 		gicv2_save_cpuif_context();
 }
@@ -55,16 +80,21 @@
 {
 	if (gicv3_detected)
 		gicv3_restore_cpuif_context();
+	else if (gicv5_detected)
+		gicv5_restore_cpuif_context();
 	else
 		gicv2_restore_cpuif_context();
 }
 
 void arm_gic_save_context_global(void)
 {
-	if (gicv3_detected)
+	if (gicv3_detected) {
 		gicv3_save_sgi_ppi_context();
-	else
+	} else if (gicv5_detected) {
+		/* NOP, done by EL3 */
+	} else {
 		gicv2_save_sgi_ppi_context();
+	}
 }
 
 void arm_gic_restore_context_global(void)
@@ -72,6 +102,8 @@
 	if (gicv3_detected) {
 		gicv3_setup_distif();
 		gicv3_restore_sgi_ppi_context();
+	} else if (gicv5_detected) {
+		/* NOP, done by EL3 */
 	} else {
 		gicv2_setup_distif();
 		gicv2_restore_sgi_ppi_context();
@@ -82,6 +114,8 @@
 {
 	if (gicv3_detected)
 		gicv3_setup_distif();
+	else if (gicv5_detected)
+		gicv5_setup();
 	else
 		gicv2_setup_distif();
 }
@@ -90,7 +124,11 @@
 {
 	if (gicv3_detected)
 		return gicv3_get_ipriorityr(num);
-	else
+	/* TODO only used for SDEI, currently not supported/ported */
+	else if (gicv5_detected) {
+		assert(0);
+		return 0;
+	} else
 		return gicv2_gicd_get_ipriorityr(num);
 }
 
@@ -99,14 +137,27 @@
 {
 	if (gicv3_detected)
 		gicv3_set_ipriorityr(num, priority);
+	else if (gicv5_detected)
+		gicv5_set_priority(num, priority);
 	else
 		gicv2_gicd_set_ipriorityr(num, priority);
 }
 
+uint32_t arm_gic_get_sgi_num(uint32_t seq_id, unsigned int core_pos)
+{
+	if (gicv5_detected) {
+		return gicv5_get_sgi_num(seq_id, core_pos);
+	} else {
+		return gicv2v3_get_sgi_num(seq_id, core_pos);
+	}
+}
+
 void arm_gic_send_sgi(unsigned int sgi_id, unsigned int core_pos)
 {
 	if (gicv3_detected)
 		gicv3_send_sgi(sgi_id, core_pos);
+	else if (gicv5_detected)
+		gicv5_send_sgi(sgi_id, core_pos);
 	else
 		gicv2_send_sgi(sgi_id, core_pos);
 }
@@ -115,6 +166,8 @@
 {
 	if (gicv3_detected)
 		gicv3_set_intr_route(num, core_pos);
+	else if (gicv5_detected)
+		gicv5_set_intr_route(num, core_pos);
 	else
 		gicv2_set_itargetsr(num, core_pos);
 }
@@ -123,7 +176,11 @@
 {
 	if (gicv3_detected)
 		return gicv3_get_isenabler(num) != 0;
-	else
+	/* TODO only used for SDEI, currently not supported/ported */
+	else if (gicv5_detected) {
+		assert(0);
+		return 0;
+	} else
 		return gicv2_gicd_get_isenabler(num) != 0;
 }
 
@@ -131,6 +188,8 @@
 {
 	if (gicv3_detected)
 		gicv3_set_isenabler(num);
+	else if (gicv5_detected)
+		gicv5_intr_enable(num);
 	else
 		gicv2_gicd_set_isenabler(num);
 }
@@ -139,6 +198,8 @@
 {
 	if (gicv3_detected)
 		gicv3_set_icenabler(num);
+	else if (gicv5_detected)
+		gicv5_intr_disable(num);
 	else
 		gicv2_gicd_set_icenabler(num);
 }
@@ -150,6 +211,9 @@
 	if (gicv3_detected) {
 		*raw_iar = gicv3_acknowledge_interrupt();
 		return *raw_iar;
+	} else if (gicv5_detected) {
+		*raw_iar = gicv5_acknowledge_interrupt();
+		return *raw_iar;
 	} else {
 		*raw_iar = gicv2_gicc_read_iar();
 		return get_gicc_iar_intid(*raw_iar);
@@ -160,6 +224,8 @@
 {
 	if (gicv3_detected)
 		return gicv3_get_ispendr(num);
+	else if (gicv5_detected)
+		return gicv5_is_intr_pending(num);
 	else
 		return gicv2_gicd_get_ispendr(num);
 }
@@ -168,6 +234,8 @@
 {
 	if (gicv3_detected)
 		gicv3_set_icpendr(num);
+	else if (gicv5_detected)
+		gicv5_intr_clear(num);
 	else
 		gicv2_gicd_set_icpendr(num);
 }
@@ -176,28 +244,44 @@
 {
 	if (gicv3_detected)
 		gicv3_end_of_interrupt(raw_iar);
+	else if (gicv5_detected)
+		gicv5_end_of_interrupt(raw_iar);
 	else
 		gicv2_gicc_write_eoir(raw_iar);
 }
 
+void arm_gic_probe(void)
+{
+	if (is_gicv3_mode()) {
+		gicv3_detected = 1;
+		INFO("GICv3 mode detected\n");
+	} else if (is_gicv5_mode()) {
+		gicv5_detected = 1;
+		INFO("GICv5 mode detected\n");
+	} else {
+		INFO("GICv2 mode detected\n");
+	}
+
+	gic_done_init = true;
+}
+
+/* to not change the API, pretend the IRS is a distributor */
 void arm_gic_init(uintptr_t gicc_base,
 		uintptr_t gicd_base,
 		uintptr_t gicr_base)
 {
-
-	if (is_gicv3_mode()) {
-		gicv3_detected = 1;
+	if (gicv3_detected) {
 		gicv3_init(gicr_base, gicd_base);
+	} else if (gicv5_detected) {
+		gicv5_init(gicd_base);
 	} else {
 		gicv2_init(gicc_base, gicd_base);
 	}
-
-	INFO("%s mode detected\n", (gicv3_detected) ?
-			"GICv3" : "GICv2");
 }
 
 bool arm_gic_is_espi_supported(void)
 {
+	/* TODO only used for FFA, currently not supported/ported on GICv5 */
 	if (!gicv3_detected) {
 		return false;
 	}
@@ -209,3 +293,21 @@
 
 	return false;
 }
+
+irq_handler_t *arm_gic_get_irq_handler(unsigned int irq_num)
+{
+	if (gicv5_detected) {
+		return gicv5_get_irq_handler(irq_num);
+	} else {
+		return gicv2v3_get_irq_handler(irq_num);
+	}
+}
+
+bool arm_gic_is_irq_shared(unsigned int irq_num)
+{
+	if (gicv5_detected) {
+		return gicv5_is_irq_spi(irq_num);
+	} else {
+		return gicv2v3_is_irq_spi(irq_num);
+	}
+}
diff --git a/drivers/arm/gic/gic_v2.c b/drivers/arm/gic/gic_v2.c
index f5fdc44..4a93406 100644
--- a/drivers/arm/gic/gic_v2.c
+++ b/drivers/arm/gic/gic_v2.c
@@ -364,4 +364,6 @@
 	assert(!is_gicv3_mode());
 	gicc_base_addr = gicc_base;
 	gicd_base_addr = gicd_base;
+
+	gicv2v3_irq_setup();
 }
diff --git a/drivers/arm/gic/gic_v2v3_common.c b/drivers/arm/gic/gic_v2v3_common.c
index 4eafa59..a929b92 100644
--- a/drivers/arm/gic/gic_v2v3_common.c
+++ b/drivers/arm/gic/gic_v2v3_common.c
@@ -11,6 +11,7 @@
 #include <drivers/arm/gic_v2v3_common.h>
 #include <drivers/arm/gic_v3.h>
 #include <mmio.h>
+#include <platform.h>
 
 /*******************************************************************************
  * GIC Distributor interface accessors for reading entire registers
@@ -197,3 +198,46 @@
 	/* Check whether the system register interface is enabled */
 	return !!is_sre_enabled();
 }
+
+bool gicv2v3_is_irq_spi(unsigned int irq_num)
+{
+	return IS_PLAT_SPI(irq_num);
+}
+
+static spi_desc spi_desc_table[PLAT_MAX_SPI_OFFSET_ID + 1];
+static ppi_desc ppi_desc_table[PLATFORM_CORE_COUNT][
+				(MAX_PPI_ID + 1) - MIN_PPI_ID];
+static sgi_desc sgi_desc_table[PLATFORM_CORE_COUNT][MAX_SGI_ID + 1];
+static spurious_desc spurious_desc_handler;
+
+void gicv2v3_irq_setup(void)
+{
+	memset(spi_desc_table, 0, sizeof(spi_desc_table));
+	memset(ppi_desc_table, 0, sizeof(ppi_desc_table));
+	memset(sgi_desc_table, 0, sizeof(sgi_desc_table));
+	memset(&spurious_desc_handler, 0, sizeof(spurious_desc_handler));
+}
+
+irq_handler_t *gicv2v3_get_irq_handler(unsigned int irq_num)
+{
+	if (IS_PLAT_SPI(irq_num)) {
+		return &spi_desc_table[irq_num - MIN_SPI_ID].handler;
+	}
+
+	unsigned int linear_id = platform_get_core_pos(read_mpidr_el1());
+
+	if (IS_PPI(irq_num)) {
+		return &ppi_desc_table[linear_id][irq_num - MIN_PPI_ID].handler;
+	}
+
+	if (IS_SGI(irq_num)) {
+		return &sgi_desc_table[linear_id][irq_num - MIN_SGI_ID].handler;
+	}
+
+	/*
+	 * The only possibility is for it to be a spurious
+	 * interrupt.
+	 */
+	assert(irq_num == GIC_SPURIOUS_INTERRUPT);
+	return &spurious_desc_handler;
+}
diff --git a/drivers/arm/gic/gic_v3.c b/drivers/arm/gic/gic_v3.c
index 8bbea48..953ea38 100644
--- a/drivers/arm/gic/gic_v3.c
+++ b/drivers/arm/gic/gic_v3.c
@@ -351,6 +351,11 @@
 	assert(core_pos < PLATFORM_CORE_COUNT);
 	assert(mpidr_list[core_pos] != UINT64_MAX);
 
+	/* only relevant for SPIs */
+	if (!IS_PLAT_SPI(interrupt_id)) {
+		return;
+	}
+
 	/* Routing information can be set only for SPIs */
 	assert(IS_SPI(interrupt_id));
 	route_affinity = mpidr_list[core_pos];
@@ -501,6 +506,8 @@
 
 	gicr_base_addr = gicr_base;
 	gicd_base_addr = gicd_base;
+
+	gicv2v3_irq_setup();
 }
 
 unsigned int gicv3_get_gicd_typer(void)
diff --git a/include/drivers/arm/arm_gic.h b/include/drivers/arm/arm_gic.h
index 528ec6e..1cb45fe 100644
--- a/include/drivers/arm/arm_gic.h
+++ b/include/drivers/arm/arm_gic.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -10,33 +10,16 @@
 #include <stdbool.h>
 #include <stdint.h>
 
-/***************************************************************************
- * Defines and prototypes for ARM GIC driver.
- **************************************************************************/
-#define MAX_SGIS		16
-#define MIN_SGI_ID		0
-#define MAX_SGI_ID		15
-#define MIN_PPI_ID		16
-#define MAX_PPI_ID		31
-#define MIN_SPI_ID		32
-#define MAX_SPI_ID		1019
-
-#define IS_SGI(irq_num)							\
-	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SGI_ID))
-
-#define IS_PPI(irq_num)							\
-	(((irq_num) >= MIN_PPI_ID) && ((irq_num) <= MAX_PPI_ID))
-
-#define IS_SPI(irq_num)							\
-	(((irq_num) >= MIN_SPI_ID) && ((irq_num) <= MAX_SPI_ID))
-
-#define IS_VALID_INTR_ID(irq_num)					\
-	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SPI_ID))
-
 #define GIC_HIGHEST_NS_PRIORITY	0
 #define GIC_LOWEST_NS_PRIORITY	254 /* 255 would disable an interrupt */
 #define GIC_SPURIOUS_INTERRUPT	1023
 
+/* Prototype of a handler function for an IRQ */
+typedef int (*irq_handler_t)(void *data);
+
+/* return the GIC version detected */
+int arm_gic_get_version(void);
+
 /******************************************************************************
  * Setup the global GIC interface. In case of GICv2, it would be the GIC
  * Distributor and in case of GICv3 it would be GIC Distributor and
@@ -65,6 +48,12 @@
 void arm_gic_send_sgi(unsigned int sgi_id, unsigned int core_pos);
 
 /******************************************************************************
+ * Get the INTID for an SGI with number `seq_id` for a core with index
+ * `core_pos`.
+ *****************************************************************************/
+unsigned int arm_gic_get_sgi_num(unsigned int seq_id, unsigned int core_pos);
+
+/******************************************************************************
  * Set the interrupt target of interrupt ID `num` to a core with index
  * `core_pos`
  *****************************************************************************/
@@ -121,6 +110,11 @@
 void arm_gic_intr_clear(unsigned int num);
 
 /******************************************************************************
+ * Discover the GIC version in use.
+ *****************************************************************************/
+void arm_gic_probe(void);
+
+/******************************************************************************
  * Initialize the GIC Driver. This function will detect the GIC Architecture
  * present on the system and initialize the appropriate driver. The
  * `gicr_base` argument will be ignored on GICv2 systems.
@@ -156,4 +150,15 @@
  *****************************************************************************/
 bool arm_gic_is_espi_supported(void);
 
+/******************************************************************************
+ * Gets the handler for an interrupt
+ *****************************************************************************/
+irq_handler_t *arm_gic_get_irq_handler(unsigned int irq_num);
+
+/******************************************************************************
+ * Returns true if the IRQ number is shared between cores (as opposed to
+ * individual or banked for each).
+ *****************************************************************************/
+bool arm_gic_is_irq_shared(unsigned int irq_num);
+
 #endif /* __ARM_GIC_H__ */
diff --git a/include/drivers/arm/gic_v2v3_common.h b/include/drivers/arm/gic_v2v3_common.h
index 7bcae6a..761ae01 100644
--- a/include/drivers/arm/gic_v2v3_common.h
+++ b/include/drivers/arm/gic_v2v3_common.h
@@ -9,6 +9,34 @@
 
 #include <stdbool.h>
 
+#include <drivers/arm/arm_gic.h>
+
+/***************************************************************************
+ * Defines and prototypes for ARM GIC driver.
+ **************************************************************************/
+#define MIN_SGI_ID		0
+#define MAX_SGI_ID		15
+#define MIN_PPI_ID		16
+#define MAX_PPI_ID		31
+#define MIN_SPI_ID		32
+#define MAX_SPI_ID		1019
+
+#define IS_SGI(irq_num)							\
+	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SGI_ID))
+
+#define IS_PPI(irq_num)							\
+	(((irq_num) >= MIN_PPI_ID) && ((irq_num) <= MAX_PPI_ID))
+
+#define IS_SPI(irq_num)							\
+	(((irq_num) >= MIN_SPI_ID) && ((irq_num) <= MAX_SPI_ID))
+
+#define IS_PLAT_SPI(irq_num)						\
+	(((irq_num) >= MIN_SPI_ID) &&					\
+	 ((irq_num) <= MIN_SPI_ID + PLAT_MAX_SPI_OFFSET_ID))
+
+#define IS_VALID_INTR_ID(irq_num)					\
+	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SPI_ID))
+
 /***************************************************************************
  * Defines and prototypes common to GIC v2 and v3 drivers.
  **************************************************************************/
@@ -90,6 +118,16 @@
 void gicd_set_icactiver(uintptr_t base, unsigned int interrupt_id);
 void gicd_set_ipriorityr(uintptr_t base, unsigned int interrupt_id,
 					unsigned int priority);
+bool gicv2v3_is_irq_spi(unsigned int irq_num);
+void gicv2v3_irq_setup(void);
+irq_handler_t *gicv2v3_get_irq_handler(unsigned int irq_num);
+
+static inline unsigned int gicv2v3_get_sgi_num(unsigned int irq_num,
+						unsigned int core_pos)
+{
+	/* the SGI index is the INTID */
+	return irq_num;
+}
 
 /*******************************************************************************
  * Private GIC Distributor interface accessors for reading and writing
diff --git a/include/drivers/arm/gic_v5.h b/include/drivers/arm/gic_v5.h
new file mode 100644
index 0000000..e8f3734
--- /dev/null
+++ b/include/drivers/arm/gic_v5.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __GIC_V5_H__
+#define __GIC_V5_H__
+
+#include <drivers/arm/arm_gic.h>
+#include <lib/mmio.h>
+
+#define INT_PPI					1
+#define INT_LPI					2
+#define INT_SPI					3
+
+#define GICV5_MAX_PPI_ID			128
+
+#define GICV5_IDLE_PRIORITY			0xff
+
+#define INT_TYPE_SHIFT				U(29)
+#define INT_TYPE_WIDTH				U(3)
+#define INT_ID_SHIFT				U(0)
+#define INT_ID_WIDTH				U(24)
+
+#define IRM_TARGETTED				0
+#define IRM_1OFN				1
+
+#define HM_EDGE					0
+#define HM_LEVEL				1
+
+struct l2_iste {
+	uint16_t iaffid;
+	uint8_t priority	: 5;
+	uint8_t hwu		: 2;
+	uint8_t res0		: 4;
+	uint8_t irm		: 1;
+	uint8_t enable		: 1;
+	uint8_t hm		: 1;
+	uint8_t active		: 1;
+	uint8_t pending		: 1;
+};
+
+#define DEFINE_GICV5_MMIO_WRITE_FUNC(_name, _offset)				\
+static inline void write_##_name(uintptr_t base, uint32_t val)			\
+{										\
+	mmio_write_32(base + _offset, val);					\
+}
+
+#define DEFINE_GICV5_MMIO_READ_FUNC(_name, _offset)				\
+static inline uint32_t read_##_name(uintptr_t base)				\
+{										\
+	return mmio_read_32(base + _offset);					\
+}
+
+#define DEFINE_GICV5_MMIO_RW_FUNCS(_name, _offset)				\
+	DEFINE_GICV5_MMIO_READ_FUNC(_name, _offset)				\
+	DEFINE_GICV5_MMIO_WRITE_FUNC(_name, _offset)
+
+#define IRS_IDR2_MIN_LPI_ID_BITS_WIDTH		UL(4)
+#define IRS_IDR2_MIN_LPI_ID_BITS_SHIFT		U(5)
+#define IRS_IDR2_ID_BITS_WIDTH			UL(5)
+#define IRS_IDR2_ID_BITS_SHIFT			U(0)
+#define IRS_CR0_IRSEN_BIT			BIT(0)
+#define IRS_CR0_IDLE_BIT			BIT(1)
+#define IRS_IST_STATUSR_IDLE_BIT		BIT(0)
+#define IRS_IST_BASER_VALID_BIT			BIT(0)
+#define IRS_IST_BASER_ADDR_SHIFT		6UL
+#define IRS_IST_BASER_ADDR_WIDTH		50UL
+
+DEFINE_GICV5_MMIO_READ_FUNC(IRS_IDR2,				0x0008)
+DEFINE_GICV5_MMIO_RW_FUNCS(  IRS_CR0,				0x0080)
+DEFINE_GICV5_MMIO_RW_FUNCS(  IRS_IST_BASER,			0x0180)
+DEFINE_GICV5_MMIO_RW_FUNCS(  IRS_IST_CFGR,			0x0190)
+DEFINE_GICV5_MMIO_RW_FUNCS(  IRS_IST_STATUSR,			0x0194)
+
+#define WAIT_FOR_IDLE(base, reg)						\
+	do {									\
+		while ((read_##reg(base) & reg##_IDLE_BIT) == 0) {}		\
+	} while (0)
+
+#ifdef __aarch64__
+bool is_gicv5_mode(void);
+bool gicv5_is_irq_spi(unsigned int interrupt_id);
+void gicv5_enable_cpuif(void);
+void gicv5_setup_cpuif(void);
+void gicv5_disable_cpuif(void);
+void gicv5_save_cpuif_context(void);
+void gicv5_restore_cpuif_context(void);
+void gicv5_set_priority(unsigned int interrupt_id, unsigned int priority);
+void gicv5_send_sgi(unsigned int sgi_id, unsigned int core_pos);
+void gicv5_set_intr_route(unsigned int interrupt_id, unsigned int core_pos);
+void gicv5_intr_enable(unsigned int interrupt_id);
+void gicv5_intr_disable(unsigned int interrupt_id);
+unsigned int gicv5_acknowledge_interrupt(void);
+unsigned int gicv5_is_intr_pending(unsigned int interrupt_id);
+void gicv5_intr_clear(unsigned int interrupt_id);
+void gicv5_end_of_interrupt(unsigned int raw_iar);
+void gicv5_setup(void);
+uint32_t gicv5_get_sgi_num(uint32_t index, unsigned int core_pos);
+irq_handler_t *gicv5_get_irq_handler(unsigned int interrupt_id);
+void gicv5_init(uintptr_t irs_base_addr);
+#else
+static inline bool is_gicv5_mode(void) { return false; }
+static inline bool gicv5_is_irq_spi(unsigned int interrupt_id) { return false; }
+static inline void gicv5_enable_cpuif(void) {}
+static inline void gicv5_setup_cpuif(void) {}
+static inline void gicv5_disable_cpuif(void) {}
+static inline void gicv5_save_cpuif_context(void) {}
+static inline void gicv5_restore_cpuif_context(void) {}
+static inline void gicv5_set_priority(unsigned int interrupt_id, unsigned int priority) {}
+static inline void gicv5_send_sgi(unsigned int sgi_id, unsigned int core_pos) {}
+static inline void gicv5_set_intr_route(unsigned int interrupt_id, unsigned int core_pos) {}
+static inline void gicv5_intr_enable(unsigned int interrupt_id) {}
+static inline void gicv5_intr_disable(unsigned int interrupt_id) {}
+static inline unsigned int gicv5_acknowledge_interrupt(void) { return -1; }
+static inline unsigned int gicv5_is_intr_pending(unsigned int interrupt_id) { return -1; }
+static inline void gicv5_intr_clear(unsigned int interrupt_id) {}
+static inline void gicv5_end_of_interrupt(unsigned int raw_iar) {}
+static inline void gicv5_setup(void) {}
+static inline uint32_t gicv5_get_sgi_num(uint32_t index, unsigned int core_pos) { return 0; }
+static inline irq_handler_t *gicv5_get_irq_handler(unsigned int interrupt_id) {return NULL; }
+static inline void gicv5_init(uintptr_t irs_base_addr) {}
+#endif
+
+#endif /* __GIC_V5_H__ */
diff --git a/include/lib/aarch32/arch_features.h b/include/lib/aarch32/arch_features.h
index 44a6845..7c5aed7 100644
--- a/include/lib/aarch32/arch_features.h
+++ b/include/lib/aarch32/arch_features.h
@@ -71,4 +71,10 @@
 	return EXTRACT(ID_PFR1_GIC, read_id_pfr1()) >= 1;
 }
 
+static inline bool is_feat_gcie_supported(void)
+{
+	/* v9 only, we can't have AArch32 in EL2 */
+	return 0;
+}
+
 #endif /* ARCH_FEATURES_H */
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index c73c68f..fbef12f 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -111,6 +111,58 @@
 #define ICV_PMR_EL1		S3_0_C4_C6_0
 
 /*******************************************************************************
+ * Definitions for CPU system register interface to GICv5
+ ******************************************************************************/
+#define ICC_CR0_EL1		S3_1_C12_C0_1
+#define ICC_PCR_EL1		S3_1_C12_C0_2
+#define ICC_IAFFIDR_EL1		S3_0_C12_C10_5
+#define ICC_ICSR_EL1		S3_0_C12_C10_4
+
+#define ICC_PPI_ENABLER0	S3_0_C12_C10_6
+#define ICC_PPI_ENABLER1	S3_0_C12_C10_7
+
+#define ICC_PPI_CPENDR0		S3_0_C12_C13_4
+#define ICC_PPI_CPENDR1		S3_0_C12_C13_5
+#define ICC_PPI_SPENDR0		S3_0_C12_C13_6
+#define ICC_PPI_SPENDR1		S3_0_C12_C13_7
+
+#define ICC_PPI_CACTIVER0	S3_0_C12_C13_0
+#define ICC_PPI_CACTIVER1	S3_0_C12_C13_1
+#define ICC_PPI_SACTIVER0	S3_0_C12_C13_2
+#define ICC_PPI_SACTIVER1	S3_0_C12_C13_3
+
+#define ICC_PPI_PRIORITYR0	S3_0_C12_C14_0
+#define ICC_PPI_PRIORITYR1	S3_0_C12_C14_1
+#define ICC_PPI_PRIORITYR2	S3_0_C12_C14_2
+#define ICC_PPI_PRIORITYR3	S3_0_C12_C14_3
+#define ICC_PPI_PRIORITYR4	S3_0_C12_C14_4
+#define ICC_PPI_PRIORITYR5	S3_0_C12_C14_5
+#define ICC_PPI_PRIORITYR6	S3_0_C12_C14_6
+#define ICC_PPI_PRIORITYR7	S3_0_C12_C14_7
+#define ICC_PPI_PRIORITYR8	S3_0_C12_C15_0
+#define ICC_PPI_PRIORITYR9	S3_0_C12_C15_1
+#define ICC_PPI_PRIORITYR10	S3_0_C12_C15_2
+#define ICC_PPI_PRIORITYR11	S3_0_C12_C15_3
+#define ICC_PPI_PRIORITYR12	S3_0_C12_C15_4
+#define ICC_PPI_PRIORITYR13	S3_0_C12_C15_5
+#define ICC_PPI_PRIORITYR14	S3_0_C12_C15_6
+#define ICC_PPI_PRIORITYR15	S3_0_C12_C15_7
+
+#define ICC_CR0_EL1_EN_BIT			BIT(0)
+#define ICC_ICSR_EL1_F_BIT			BIT(0)
+#define ICC_ICSR_EL1_PENDING_BIT		BIT(2)
+#define ICC_PPI_PRIORITYR_FIELD_NUM		8UL
+#define ICC_PPI_PRIORITYR_FIELD_MASK		GENMASK(7, 0)
+#define ICC_PPI_ENABLER_FIELD_NUM		64UL
+#define ICC_PPI_XPENDR_FIELD_NUM		64UL
+
+#define GICCDAFF_IAFFID_SHIFT			32UL
+#define GICCDPRI_PRIORITY_SHIFT			35UL
+#define GICCDPRI_PRIORITY_WIDTH			5UL
+#define GICCDPEND_PENDING_BIT			BIT(32)
+#define GICRCDIA_VALID_BIT			BIT(32)
+
+/*******************************************************************************
  * Definitions for EL2 system registers.
  ******************************************************************************/
 #define CNTPOFF_EL2		S3_4_C14_C0_6
@@ -568,6 +620,11 @@
 #define ID_AA64PFR2_EL1_FPMR_WIDTH		U(4)
 #define ID_AA64PFR2_EL1_FPMR_SUPPORTED		ULL(0x1)
 
+#define ID_AA64PFR2_EL1_GCIE_SHIFT		U(12)
+#define ID_AA64PFR2_EL1_GCIE_MASK		ULL(0xf)
+#define ID_AA64PFR2_EL1_GCIE_WIDTH		U(4)
+#define ID_AA64PFR2_EL1_GCIE_SUPPORTED		ULL(0x1)
+
 /* ID_PFR1_EL1 definitions */
 #define ID_PFR1_VIRTEXT_SHIFT	U(12)
 #define ID_PFR1_VIRTEXT_MASK	U(0xf)
diff --git a/include/lib/aarch64/arch_features.h b/include/lib/aarch64/arch_features.h
index eb846cd..a873501 100644
--- a/include/lib/aarch64/arch_features.h
+++ b/include/lib/aarch64/arch_features.h
@@ -619,4 +619,10 @@
 	return EXTRACT(ID_AA64PFR0_GIC, read_id_aa64pfr0_el1())
 			>= ID_AA64PFR0_GICV3_GICV4_SUPPORTED;
 }
+
+static inline bool is_feat_gcie_supported(void)
+{
+	return EXTRACT(ID_AA64PFR2_EL1_GCIE, read_id_aa64pfr2_el1())
+			>= ID_AA64PFR2_EL1_GCIE_SUPPORTED;
+}
 #endif /* ARCH_FEATURES_H */
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index 76e2431..13b9a4f 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -89,6 +89,28 @@
 	 __asm__ (#_op " " #_type ", %0" : : "r" (v));	\
 }
 
+/* Define an instruction with an encoding. Useful when no compiler support */
+#define DEFINE_INSN(_op, _type, _op1, _CRn, _CRm, _op2)				\
+static inline void _op ## _type(void)						\
+{										\
+	__asm__ ("sys " #_op1 ", " #_CRn ", " #_CRm ", " #_op2 : : : "memory");	\
+}
+
+#define DEFINE_INSN_RET(_op, _type, _op1, _CRn, _CRm, _op2)			\
+static inline u_register_t _op ## _type(void)					\
+{										\
+	u_register_t v;								\
+	__asm__ ("sysl %0, " #_op1 ", " #_CRn ", " #_CRm ", " #_op2 : "=r" (v));\
+	return v;								\
+}
+
+/* Define an instruction with an encoding. Useful when no compiler support */
+#define DEFINE_INSN_PARAM(_op, _type, _op1, _CRn, _CRm, _op2)			\
+static inline void _op ## _type(u_register_t v)					\
+{										\
+	__asm__ ("sys " #_op1 ", " #_CRn ", " #_CRm ", " #_op2 ", %0" : : "r" (v));\
+}
+
 /*******************************************************************************
  * TLB maintenance accessor prototypes
  ******************************************************************************/
@@ -167,6 +189,25 @@
 DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e2r)
 DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e3r)
 
+/*******************************************************************************
+ * GICv5 system instructions for the Current Interrupt Domain
+ ******************************************************************************/
+DEFINE_INSN_PARAM(gic, cddis,  0, c12, c1, 0)
+DEFINE_INSN_PARAM(gic, cden,   0, c12, c1, 1)
+DEFINE_INSN_PARAM(gic, cdpri,  0, c12, c1, 2)
+DEFINE_INSN_PARAM(gic, cdaff,  0, c12, c1, 3)
+DEFINE_INSN_PARAM(gic, cdpend, 0, c12, c1, 4)
+DEFINE_INSN_PARAM(gic, cdrcfg, 0, c12, c1, 5)
+DEFINE_INSN_RET(gicr,  cdia,   0, c12, c3, 0)
+DEFINE_INSN_PARAM(gic, cddi,   0, c12, c2, 0)
+DEFINE_INSN(gic,       cdeoi,  0, c12, c1, 7)
+
+/*******************************************************************************
+ * GICv5 barriers
+ ******************************************************************************/
+DEFINE_INSN(gsb,       sys,    0, c12, c0, 0)
+DEFINE_INSN(gsb,       ack,    0, c12, c0, 1)
+
 void flush_dcache_range(uintptr_t addr, size_t size);
 void clean_dcache_range(uintptr_t addr, size_t size);
 void inv_dcache_range(uintptr_t addr, size_t size);
@@ -520,6 +561,42 @@
 DEFINE_RENAME_SYSREG_RW_FUNCS(amcntenclr1_el0, AMCNTENCLR1_EL0)
 DEFINE_RENAME_SYSREG_RW_FUNCS(amcntenset1_el0, AMCNTENSET1_EL0)
 
+/* GICv5 System Registers */
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_cr0_el1, ICC_CR0_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_pcr_el1, ICC_PCR_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(icc_iaffidr_el1, ICC_IAFFIDR_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(icc_icsr_el1, ICC_ICSR_EL1)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_enabler0, ICC_PPI_ENABLER0)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_enabler1, ICC_PPI_ENABLER1)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_cpendr0, ICC_PPI_CPENDR0)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_cpendr1, ICC_PPI_CPENDR1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_spendr0, ICC_PPI_SPENDR0)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_spendr1, ICC_PPI_SPENDR1)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_cactiver0, ICC_PPI_CACTIVER0)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_cactiver1, ICC_PPI_CACTIVER1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_sactiver0, ICC_PPI_SACTIVER0)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_sactiver1, ICC_PPI_SACTIVER1)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr0, ICC_PPI_PRIORITYR0)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr1, ICC_PPI_PRIORITYR1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr2, ICC_PPI_PRIORITYR2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr3, ICC_PPI_PRIORITYR3)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr4, ICC_PPI_PRIORITYR4)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr5, ICC_PPI_PRIORITYR5)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr6, ICC_PPI_PRIORITYR6)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr7, ICC_PPI_PRIORITYR7)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr8, ICC_PPI_PRIORITYR8)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr9, ICC_PPI_PRIORITYR9)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr10, ICC_PPI_PRIORITYR10)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr11, ICC_PPI_PRIORITYR11)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr12, ICC_PPI_PRIORITYR12)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr13, ICC_PPI_PRIORITYR13)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr14, ICC_PPI_PRIORITYR14)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ppi_priorityr15, ICC_PPI_PRIORITYR15)
+
 /* Armv8.4 Memory Partitioning and Monitoring Extension Registers */
 DEFINE_RENAME_SYSREG_READ_FUNC(mpamidr_el1, MPAMIDR_EL1)
 DEFINE_RENAME_SYSREG_RW_FUNCS(mpam3_el3, MPAM3_EL3)
diff --git a/include/lib/irq.h b/include/lib/irq.h
index 17993a8..cdd34df 100644
--- a/include/lib/irq.h
+++ b/include/lib/irq.h
@@ -10,18 +10,30 @@
 #include <cdefs.h>
 #include <platform_def.h> /* For CACHE_WRITEBACK_GRANULE */
 #include <stdint.h>
+#include <drivers/arm/arm_gic.h>
+
+/*******************************************************************************
+ * Non-Secure Software Generated Interupts IDs
+ ******************************************************************************/
+#define IRQ_NS_SGI_0			0
+#define IRQ_NS_SGI_1			1
+#define IRQ_NS_SGI_2			2
+#define IRQ_NS_SGI_3			3
+#define IRQ_NS_SGI_4			4
+#define IRQ_NS_SGI_5			5
+#define IRQ_NS_SGI_6			6
+#define IRQ_NS_SGI_7			7
+#define IRQ_NUM_SGIS			(IRQ_NS_SGI_7 + 1)
 
 /*
  * SGI sent by the timer management framework to notify CPUs when the system
  * timer fires off
  */
 #define IRQ_WAKE_SGI		IRQ_NS_SGI_7
+#define IRQ_NUM_SGIS		(IRQ_NS_SGI_7 + 1)
 
 #ifndef __ASSEMBLY__
 
-/* Prototype of a handler function for an IRQ */
-typedef int (*irq_handler_t)(void *data);
-
 /* Keep track of the IRQ handler registered for a given SPI */
 typedef struct {
 	irq_handler_t handler;
@@ -50,6 +62,11 @@
 void tftf_irq_setup(void);
 
 /*
+ * Get the INTID for an SGI with sequential number seq_id
+ */
+unsigned int tftf_irq_get_my_sgi_num(unsigned int seq_id);
+
+/*
  * Generic handler called upon reception of an IRQ.
  *
  * This function acknowledges the interrupt, calls the user-defined handler
@@ -67,11 +84,13 @@
  * Enable interrupt #irq_num for the calling core.
  */
 void tftf_irq_enable(unsigned int irq_num, uint8_t irq_priority);
+void tftf_irq_enable_sgi(unsigned int sgi_id, uint8_t irq_priority);
 
 /*
  * Disable interrupt #irq_num for the calling core.
  */
 void tftf_irq_disable(unsigned int irq_num);
+void tftf_irq_disable_sgi(unsigned int sgi_id);
 
 /*
  * Register an interrupt handler for a given interrupt number.
@@ -81,6 +100,7 @@
  * Return 0 on success, a negative value otherwise.
  */
 int tftf_irq_register_handler(unsigned int num, irq_handler_t irq_handler);
+int tftf_irq_register_handler_sgi(unsigned int sgi_id, irq_handler_t irq_handler);
 
 /*
  * Unregister an interrupt handler for a given interrupt number.
@@ -89,6 +109,7 @@
  * Return 0 on success, a negative value otherwise.
  */
 int tftf_irq_unregister_handler(unsigned int irq_num);
+int tftf_irq_unregister_handler_sgi(unsigned int sgi_id);
 
 #endif /* __ASSEMBLY__ */
 
diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h
index c9bd527..ae85a81 100644
--- a/include/lib/utils_def.h
+++ b/include/lib/utils_def.h
@@ -233,4 +233,14 @@
 							      0;  \
 	}))
 
+/*
+ * Get the next power of 2 for x. Eg. next_power_of_2(12) is 16
+ */
+#define b2(x)   ((x)    | ((x)    >> 1))
+#define b4(x)   (b2(x)  | (b2(x)  >> 2))
+#define b8(x)   (b4(x)  | (b4(x)  >> 4))
+#define b16(x)  (b8(x)  | (b8(x)  >> 8))
+#define b32(x)  (b16(x) | (b16(x) >> 16))
+#define next_power_of_2(x)      (b32(x - 1) + 1)
+
 #endif /* UTILS_DEF_H */
diff --git a/lib/exceptions/irq.c b/lib/exceptions/irq.c
index b99857c..289a17a 100644
--- a/lib/exceptions/irq.c
+++ b/lib/exceptions/irq.c
@@ -18,16 +18,6 @@
 #include <tftf.h>
 #include <tftf_lib.h>
 
-#define IS_PLAT_SPI(irq_num)						\
-	(((irq_num) >= MIN_SPI_ID) &&					\
-	 ((irq_num) <= MIN_SPI_ID + PLAT_MAX_SPI_OFFSET_ID))
-
-static spi_desc spi_desc_table[PLAT_MAX_SPI_OFFSET_ID + 1];
-static ppi_desc ppi_desc_table[PLATFORM_CORE_COUNT][
-				(MAX_PPI_ID + 1) - MIN_PPI_ID];
-static sgi_desc sgi_desc_table[PLATFORM_CORE_COUNT][MAX_SGI_ID + 1];
-static spurious_desc spurious_desc_handler;
-
 /*
  * For a given SPI, the associated IRQ handler is common to all CPUs.
  * Therefore, we need a lock to prevent simultaneous updates.
@@ -37,34 +27,17 @@
  * saves memory. Updating an SPI handler shouldn't occur that often anyway so we
  * shouldn't suffer from this restriction too much.
  */
-static spinlock_t spi_lock;
+static spinlock_t shared_irq_lock;
 
-static irq_handler_t *get_irq_handler(unsigned int irq_num)
+
+unsigned int tftf_irq_get_my_sgi_num(unsigned int seq_id)
 {
-	if (IS_PLAT_SPI(irq_num))
-		return &spi_desc_table[irq_num - MIN_SPI_ID].handler;
-
-	unsigned int mpid = read_mpidr_el1();
-	unsigned int linear_id = platform_get_core_pos(mpid);
-
-	if (IS_PPI(irq_num))
-		return &ppi_desc_table[linear_id][irq_num - MIN_PPI_ID].handler;
-
-	if (IS_SGI(irq_num))
-		return &sgi_desc_table[linear_id][irq_num - MIN_SGI_ID].handler;
-
-	/*
-	 * The only possibility is for it to be a spurious
-	 * interrupt.
-	 */
-	assert(irq_num == GIC_SPURIOUS_INTERRUPT);
-	return &spurious_desc_handler;
+	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
+	return arm_gic_get_sgi_num(seq_id, core_pos);
 }
 
 void tftf_send_sgi(unsigned int sgi_id, unsigned int core_pos)
 {
-	assert(IS_SGI(sgi_id));
-
 	/*
 	 * Ensure that all memory accesses prior to sending the SGI are
 	 * completed.
@@ -86,20 +59,20 @@
 
 void tftf_irq_enable(unsigned int irq_num, uint8_t irq_priority)
 {
-	if (IS_PLAT_SPI(irq_num)) {
-		/*
-		 * Instruct the GIC Distributor to forward the interrupt to
-		 * the calling core
-		 */
-		arm_gic_set_intr_target(irq_num, platform_get_core_pos(read_mpidr_el1()));
-	}
-
+	arm_gic_set_intr_target(irq_num, platform_get_core_pos(read_mpidr_el1()));
 	arm_gic_set_intr_priority(irq_num, irq_priority);
 	arm_gic_intr_enable(irq_num);
 
 	VERBOSE("Enabled IRQ #%u\n", irq_num);
 }
 
+void tftf_irq_enable_sgi(unsigned int sgi_id, uint8_t irq_priority)
+{
+	unsigned int irq_num = tftf_irq_get_my_sgi_num(sgi_id);
+
+	tftf_irq_enable(irq_num, irq_priority);
+}
+
 void tftf_irq_disable(unsigned int irq_num)
 {
 	/* Disable the interrupt */
@@ -108,6 +81,13 @@
 	VERBOSE("Disabled IRQ #%u\n", irq_num);
 }
 
+void tftf_irq_disable_sgi(unsigned int sgi_id)
+{
+	unsigned int irq_num = tftf_irq_get_my_sgi_num(sgi_id);
+
+	tftf_irq_disable(irq_num);
+}
+
 #define HANDLER_VALID(handler, expect_handler)		\
 	((expect_handler) ? ((handler) != NULL) : ((handler) == NULL))
 
@@ -118,9 +98,9 @@
 	irq_handler_t *cur_handler;
 	int ret = -1;
 
-	cur_handler = get_irq_handler(irq_num);
-	if (IS_PLAT_SPI(irq_num))
-		spin_lock(&spi_lock);
+	cur_handler = arm_gic_get_irq_handler(irq_num);
+	if (arm_gic_is_irq_shared(irq_num))
+		spin_lock(&shared_irq_lock);
 
 	/*
 	 * Update the IRQ handler, if the current handler is in the expected
@@ -132,8 +112,8 @@
 		ret = 0;
 	}
 
-	if (IS_PLAT_SPI(irq_num))
-		spin_unlock(&spi_lock);
+	if (arm_gic_is_irq_shared(irq_num))
+		spin_unlock(&shared_irq_lock);
 
 	return ret;
 }
@@ -150,6 +130,12 @@
 	return ret;
 }
 
+int tftf_irq_register_handler_sgi(unsigned int sgi_id, irq_handler_t irq_handler)
+{
+	unsigned int irq_num = tftf_irq_get_my_sgi_num(sgi_id);
+	return tftf_irq_register_handler(irq_num, irq_handler);
+}
+
 int tftf_irq_unregister_handler(unsigned int irq_num)
 {
 	int ret;
@@ -161,6 +147,12 @@
 	return ret;
 }
 
+int tftf_irq_unregister_handler_sgi(unsigned int sgi_id)
+{
+	unsigned int irq_num = tftf_irq_get_my_sgi_num(sgi_id);
+	return tftf_irq_unregister_handler(irq_num);
+}
+
 int tftf_irq_handler_dispatcher(void)
 {
 	unsigned int raw_iar;
@@ -172,7 +164,7 @@
 	/* Acknowledge the interrupt */
 	irq_num = arm_gic_intr_ack(&raw_iar);
 
-	handler = get_irq_handler(irq_num);
+	handler = arm_gic_get_irq_handler(irq_num);
 	irq_data = &irq_num;
 
 	if (*handler != NULL)
@@ -187,9 +179,5 @@
 
 void tftf_irq_setup(void)
 {
-	memset(spi_desc_table, 0, sizeof(spi_desc_table));
-	memset(ppi_desc_table, 0, sizeof(ppi_desc_table));
-	memset(sgi_desc_table, 0, sizeof(sgi_desc_table));
-	memset(&spurious_desc_handler, 0, sizeof(spurious_desc_handler));
-	init_spinlock(&spi_lock);
+	init_spinlock(&shared_irq_lock);
 }
diff --git a/lib/power_management/hotplug/hotplug.c b/lib/power_management/hotplug/hotplug.c
index 7d534b5..cb00cff 100644
--- a/lib/power_management/hotplug/hotplug.c
+++ b/lib/power_management/hotplug/hotplug.c
@@ -301,7 +301,7 @@
 	arm_gic_setup_local();
 
 	/* Enable the SGI used by the timer management framework */
-	tftf_irq_enable(IRQ_WAKE_SGI, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_WAKE_SGI, GIC_HIGHEST_NS_PRIORITY);
 	tftf_initialise_timer_secondary_core();
 
 	enable_irq();
diff --git a/lib/psci/psci.c b/lib/psci/psci.c
index 720d319..2c86e1c 100644
--- a/lib/psci/psci.c
+++ b/lib/psci/psci.c
@@ -269,7 +269,7 @@
 		return;
 	}
 
-	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	/*
 	 * Mask IRQ to prevent the interrupt handler being invoked
@@ -298,7 +298,7 @@
 	enable_irq();
 	isb();
 
-	tftf_irq_disable(IRQ_NS_SGI_0);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_0);
 
 	/*
 	 * The NULL State-ID returned SUCCESS. Hence State-ID is NULL
diff --git a/plat/amd/versal2/include/platform_def.h b/plat/amd/versal2/include/platform_def.h
index 206f292..814ea85 100644
--- a/plat/amd/versal2/include/platform_def.h
+++ b/plat/amd/versal2/include/platform_def.h
@@ -87,18 +87,6 @@
 #define CRASH_CONSOLE_BASE			PL011_UART1_BASE
 #define CRASH_CONSOLE_SIZE			PLAT_ARM_UART_SIZE
 
-/*******************************************************************************
- * Non-Secure Software Generated Interrupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0				0
-#define IRQ_NS_SGI_1				1
-#define IRQ_NS_SGI_2				2
-#define IRQ_NS_SGI_3				3
-#define IRQ_NS_SGI_4				4
-#define IRQ_NS_SGI_5				5
-#define IRQ_NS_SGI_6				6
-#define IRQ_NS_SGI_7				7
-
 /* Per-CPU Hypervisor Timer Interrupt ID */
 #define IRQ_PCPU_HP_TIMER			U(29)
 /* Datasheet: TIME00 event*/
diff --git a/plat/arm/common/arm_timers.c b/plat/arm/common/arm_timers.c
index f43fdaa..35171c9 100644
--- a/plat/arm/common/arm_timers.c
+++ b/plat/arm/common/arm_timers.c
@@ -1,10 +1,11 @@
 /*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
 #include <assert.h>
+#include <drivers/arm/gic_v5.h>
 #include <drivers/arm/system_timer.h>
 #include <platform.h>
 #include <stddef.h>
@@ -12,12 +13,11 @@
 
 #pragma weak plat_initialise_timer_ops
 
-static const plat_timer_t plat_timers = {
+static plat_timer_t plat_timers = {
 	.program = program_systimer,
 	.cancel = cancel_systimer,
 	.handler = handler_systimer,
 	.timer_step_value = 2,
-	.timer_irq = IRQ_CNTPSIRQ1
 };
 
 int plat_initialise_timer_ops(const plat_timer_t **timer_ops)
@@ -25,6 +25,20 @@
 	assert(timer_ops != NULL);
 	*timer_ops = &plat_timers;
 
+	/*
+	 * on GICv2/3 platforms give the INTID verbatim (eg SPI 60, ie. INTID
+	 * 92) although can be a PPI too
+	 * on GICv5 take the same SPI ID but re-use the GICv3 macro for
+	 * compatibility.
+	 * TODO: currently unclear how non-fvp platforms will use this; refactor
+	 * to work elsewhere.
+	 */
+	if (arm_gic_get_version() != 5) {
+		plat_timers.timer_irq = IRQ_CNTPSIRQ1;
+	} else {
+		plat_timers.timer_irq = (IRQ_CNTPSIRQ1 - 32) | INPLACE(INT_TYPE, INT_SPI);
+	}
+
 	/* Initialise the system timer */
 	init_systimer(SYS_CNT_BASE1);
 
diff --git a/plat/arm/corstone1000/include/platform_def.h b/plat/arm/corstone1000/include/platform_def.h
index a0d6f7b..91f4cda 100644
--- a/plat/arm/corstone1000/include/platform_def.h
+++ b/plat/arm/corstone1000/include/platform_def.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -134,18 +134,6 @@
 #define CACHE_WRITEBACK_SHIFT   6
 #define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT)
 
-/*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0		0
-#define IRQ_NS_SGI_1		1
-#define IRQ_NS_SGI_2		2
-#define IRQ_NS_SGI_3		3
-#define IRQ_NS_SGI_4		4
-#define IRQ_NS_SGI_5		5
-#define IRQ_NS_SGI_6		6
-#define IRQ_NS_SGI_7		7
-
 #define PLAT_MAX_SPI_OFFSET_ID	220
 
 /* The IRQ generated by Ethernet controller */
diff --git a/plat/arm/fvp/fvp_def.h b/plat/arm/fvp/fvp_def.h
index 222fadd..1d4e507 100644
--- a/plat/arm/fvp/fvp_def.h
+++ b/plat/arm/fvp/fvp_def.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -71,6 +71,11 @@
 #define GICC_BASE		0x2c000000
 
 /*******************************************************************************
+ * GICv5 related constants
+ ******************************************************************************/
+#define IRS_BASE		0x2f1a0000
+
+/*******************************************************************************
  * PL011 related constants
  ******************************************************************************/
 #define PL011_UART0_BASE	0x1c090000
diff --git a/plat/arm/fvp/include/platform_def.h b/plat/arm/fvp/include/platform_def.h
index 322d264..089e0a8 100644
--- a/plat/arm/fvp/include/platform_def.h
+++ b/plat/arm/fvp/include/platform_def.h
@@ -274,18 +274,6 @@
 #define CACHE_WRITEBACK_SHIFT		6
 #define CACHE_WRITEBACK_GRANULE		(1 << CACHE_WRITEBACK_SHIFT)
 
-/*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_1			1
-#define IRQ_NS_SGI_2			2
-#define IRQ_NS_SGI_3			3
-#define IRQ_NS_SGI_4			4
-#define IRQ_NS_SGI_5			5
-#define IRQ_NS_SGI_6			6
-#define IRQ_NS_SGI_7			7
-
 /*
  * On FVP, consider that the last SPI is the Trusted Random Number Generator
  * interrupt.
diff --git a/plat/arm/fvp/plat_setup.c b/plat/arm/fvp/plat_setup.c
index f09bf59..c897289 100644
--- a/plat/arm/fvp/plat_setup.c
+++ b/plat/arm/fvp/plat_setup.c
@@ -52,5 +52,9 @@
 
 void plat_arm_gic_init(void)
 {
-	arm_gic_init(GICC_BASE, GICD_BASE, GICR_BASE);
+	if (arm_gic_get_version() != 5) {
+		arm_gic_init(GICC_BASE, GICD_BASE, GICR_BASE);
+	} else {
+		arm_gic_init(0, IRS_BASE, 0);
+	}
 }
diff --git a/plat/arm/juno/include/platform_def.h b/plat/arm/juno/include/platform_def.h
index 2de11fd..89e5601 100644
--- a/plat/arm/juno/include/platform_def.h
+++ b/plat/arm/juno/include/platform_def.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -225,18 +225,6 @@
 #define CACHE_WRITEBACK_SHIFT   6
 #define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT)
 
-/*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0		0
-#define IRQ_NS_SGI_1		1
-#define IRQ_NS_SGI_2		2
-#define IRQ_NS_SGI_3		3
-#define IRQ_NS_SGI_4		4
-#define IRQ_NS_SGI_5		5
-#define IRQ_NS_SGI_6		6
-#define IRQ_NS_SGI_7		7
-
 #define PLAT_MAX_SPI_OFFSET_ID	220
 
 /* The IRQ generated by Ethernet controller */
diff --git a/plat/arm/n1sdp/include/platform_def.h b/plat/arm/n1sdp/include/platform_def.h
index 71409fc..91d6b3c 100644
--- a/plat/arm/n1sdp/include/platform_def.h
+++ b/plat/arm/n1sdp/include/platform_def.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2022-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -135,10 +135,6 @@
 #define CACHE_WRITEBACK_SHIFT  		 6
 #define CACHE_WRITEBACK_GRANULE		 (1 << CACHE_WRITEBACK_SHIFT)
 
-/* Non-Secure Software Generated Interupts IDs */
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_7			7
-
 /*
  * AP UART1 interrupt is considered as the maximum SPI.
  * MAX_SPI_ID = MIN_SPI_ID + PLAT_MAX_SPI_OFFSET_ID = 96
diff --git a/plat/arm/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h b/plat/arm/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h
index ba1ddb8..26515b3 100644
--- a/plat/arm/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h
+++ b/plat/arm/neoverse_rd/common/include/nrd2/nrd_plat_arm_def2.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2024-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -76,14 +76,6 @@
 #define MAX_IO_DEVICES			U(1)
 #define MAX_IO_HANDLES			U(1)
 
-/*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-
-/* Non-Secure Software Generated Interupts IDs */
-#define IRQ_NS_SGI_0			U(0)
-#define IRQ_NS_SGI_7			U(7)
-
 /* Maximum SPI */
 #define PLAT_MAX_SPI_OFFSET_ID	U(256)
 
@@ -162,4 +154,4 @@
 #define FLASH_SIZE			NRD_ROS_FLASH_SIZE
 #define NOR_FLASH_BLOCK_SIZE		UL(0x40000)     /* 256KB */
 
-#endif /* NRD_PLAT_ARM_DEF2_H */
\ No newline at end of file
+#endif /* NRD_PLAT_ARM_DEF2_H */
diff --git a/plat/arm/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h b/plat/arm/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h
index 20c748e..5c2f59e 100644
--- a/plat/arm/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h
+++ b/plat/arm/neoverse_rd/common/include/nrd3/nrd_plat_arm_def3.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2024-2025, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  *
@@ -68,13 +68,6 @@
 #define MAX_IO_DEVICES			U(1)
 #define MAX_IO_HANDLES			U(1)
 
-/*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-
-#define IRQ_NS_SGI_0			U(0)
-#define IRQ_NS_SGI_7			U(7)
-
 /* Maximum SPI */
 #define PLAT_MAX_SPI_OFFSET_ID		U(256)
 
@@ -152,4 +145,4 @@
 #define FLASH_SIZE			NRD_ROS_FLASH_SIZE
 #define NOR_FLASH_BLOCK_SIZE		UL(0x40000)     /* 256KB */
 
-#endif /* NRD_PLAT_ARM_DEF3_H */
\ No newline at end of file
+#endif /* NRD_PLAT_ARM_DEF3_H */
diff --git a/plat/arm/tc/include/platform_def.h b/plat/arm/tc/include/platform_def.h
index 82fa6c2..28325c3 100644
--- a/plat/arm/tc/include/platform_def.h
+++ b/plat/arm/tc/include/platform_def.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020-2021, Arm Limited. All rights reserved.
+ * Copyright (c) 2020-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -129,10 +129,6 @@
 #define CACHE_WRITEBACK_SHIFT  		 6
 #define CACHE_WRITEBACK_GRANULE		 (1 << CACHE_WRITEBACK_SHIFT)
 
-/* Non-Secure Software Generated Interupts IDs */
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_7			7
-
 /* AP UART1 interrupt is considered as the maximum SPI */
 #define PLAT_MAX_SPI_OFFSET_ID		64
 
diff --git a/plat/hisilicon/hikey960/include/platform_def.h b/plat/hisilicon/hikey960/include/platform_def.h
index e3e878f..8119c51 100644
--- a/plat/hisilicon/hikey960/include/platform_def.h
+++ b/plat/hisilicon/hikey960/include/platform_def.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -80,18 +80,6 @@
 #define PL011_BAUDRATE			115200
 #define PL011_UART_CLK_IN_HZ		19200000
 
-/*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_1			1
-#define IRQ_NS_SGI_2			2
-#define IRQ_NS_SGI_3			3
-#define IRQ_NS_SGI_4			4
-#define IRQ_NS_SGI_5			5
-#define IRQ_NS_SGI_6			6
-#define IRQ_NS_SGI_7			7
-
 /* Per-CPU Hypervisor Timer Interrupt ID */
 #define IRQ_PCPU_HP_TIMER		26
 /* Datasheet: TIME00 event*/
diff --git a/plat/nvidia/tegra186/include/platform_def.h b/plat/nvidia/tegra186/include/platform_def.h
index 8ad93ad..76ebd60 100644
--- a/plat/nvidia/tegra186/include/platform_def.h
+++ b/plat/nvidia/tegra186/include/platform_def.h
@@ -104,18 +104,6 @@
 #define PLAT_SUSPEND_ENTRY_EXIT_TIME	1000
 
 /*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_1			1
-#define IRQ_NS_SGI_2			2
-#define IRQ_NS_SGI_3			3
-#define IRQ_NS_SGI_4			4
-#define IRQ_NS_SGI_5			5
-#define IRQ_NS_SGI_6			6
-#define IRQ_NS_SGI_7			7
-
-/*******************************************************************************
  * Per-CPU Hypervisor Timer Interrupt ID
  ******************************************************************************/
 #define IRQ_PCPU_HP_TIMER		26
diff --git a/plat/nvidia/tegra194/include/platform_def.h b/plat/nvidia/tegra194/include/platform_def.h
index 0cd3ecd..6d3a0bb 100644
--- a/plat/nvidia/tegra194/include/platform_def.h
+++ b/plat/nvidia/tegra194/include/platform_def.h
@@ -105,18 +105,6 @@
 #define PLAT_SUSPEND_ENTRY_EXIT_TIME	1000
 
 /*******************************************************************************
- * Non-Secure Software Generated Interupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_1			1
-#define IRQ_NS_SGI_2			2
-#define IRQ_NS_SGI_3			3
-#define IRQ_NS_SGI_4			4
-#define IRQ_NS_SGI_5			5
-#define IRQ_NS_SGI_6			6
-#define IRQ_NS_SGI_7			7
-
-/*******************************************************************************
  * Per-CPU Hypervisor Timer Interrupt ID
  ******************************************************************************/
 #define IRQ_PCPU_HP_TIMER		26
diff --git a/plat/nvidia/tegra210/include/platform_def.h b/plat/nvidia/tegra210/include/platform_def.h
index 0e369b3..29f4252 100644
--- a/plat/nvidia/tegra210/include/platform_def.h
+++ b/plat/nvidia/tegra210/include/platform_def.h
@@ -102,18 +102,6 @@
 #define PLAT_SUSPEND_ENTRY_TIME		500
 #define PLAT_SUSPEND_ENTRY_EXIT_TIME	1000
 
-/******************************************************************************
-* Non-Secure Software Generated Interupts IDs
-******************************************************************************/
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_1			1
-#define IRQ_NS_SGI_2			2
-#define IRQ_NS_SGI_3			3
-#define IRQ_NS_SGI_4			4
-#define IRQ_NS_SGI_5			5
-#define IRQ_NS_SGI_6			6
-#define IRQ_NS_SGI_7			7
-
 /*******************************************************************************
  * Per-CPU Hypervisor Timer Interrupt ID
  ******************************************************************************/
diff --git a/plat/xilinx/versal/include/platform_def.h b/plat/xilinx/versal/include/platform_def.h
index 1e6bd5d..09b4acc 100644
--- a/plat/xilinx/versal/include/platform_def.h
+++ b/plat/xilinx/versal/include/platform_def.h
@@ -85,18 +85,6 @@
 #define CRASH_CONSOLE_BASE			PL011_UART0_BASE
 #define CRASH_CONSOLE_SIZE			PLAT_ARM_UART_SIZE
 
-/*******************************************************************************
- * Non-Secure Software Generated Interrupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0				0
-#define IRQ_NS_SGI_1				1
-#define IRQ_NS_SGI_2				2
-#define IRQ_NS_SGI_3				3
-#define IRQ_NS_SGI_4				4
-#define IRQ_NS_SGI_5				5
-#define IRQ_NS_SGI_6				6
-#define IRQ_NS_SGI_7				7
-
 /* Per-CPU Hypervisor Timer Interrupt ID */
 #define IRQ_PCPU_HP_TIMER			U(29)
 /* Datasheet: TIME00 event*/
diff --git a/plat/xilinx/versal_net/include/platform_def.h b/plat/xilinx/versal_net/include/platform_def.h
index 12c5910..fce271d 100644
--- a/plat/xilinx/versal_net/include/platform_def.h
+++ b/plat/xilinx/versal_net/include/platform_def.h
@@ -86,18 +86,6 @@
 #define CRASH_CONSOLE_BASE			PL011_UART0_BASE
 #define CRASH_CONSOLE_SIZE			PLAT_ARM_UART_SIZE
 
-/*******************************************************************************
- * Non-Secure Software Generated Interrupts IDs
- ******************************************************************************/
-#define IRQ_NS_SGI_0				0
-#define IRQ_NS_SGI_1				1
-#define IRQ_NS_SGI_2				2
-#define IRQ_NS_SGI_3				3
-#define IRQ_NS_SGI_4				4
-#define IRQ_NS_SGI_5				5
-#define IRQ_NS_SGI_6				6
-#define IRQ_NS_SGI_7				7
-
 /* Per-CPU Hypervisor Timer Interrupt ID */
 #define IRQ_PCPU_HP_TIMER			U(26)
 /* Datasheet: TIME00 event*/
diff --git a/plat/xilinx/zynqmp/include/platform_def.h b/plat/xilinx/zynqmp/include/platform_def.h
index cb3a707..e7128de 100644
--- a/plat/xilinx/zynqmp/include/platform_def.h
+++ b/plat/xilinx/zynqmp/include/platform_def.h
@@ -70,17 +70,6 @@
 #define CACHE_WRITEBACK_SHIFT		6
 #define CACHE_WRITEBACK_GRANULE		(1 << CACHE_WRITEBACK_SHIFT)
 
-/* Non-Secure Software Generated Interrupts IDs */
-
-#define IRQ_NS_SGI_0			0
-#define IRQ_NS_SGI_1			1
-#define IRQ_NS_SGI_2			2
-#define IRQ_NS_SGI_3			3
-#define IRQ_NS_SGI_4			4
-#define IRQ_NS_SGI_5			5
-#define IRQ_NS_SGI_6			6
-#define IRQ_NS_SGI_7			7
-
 /* Platform specific page table and MMU setup constants */
 
 #define PLAT_PHY_ADDR_SPACE_SIZE	(ULL(1) << 32)
diff --git a/tftf/framework/framework.mk b/tftf/framework/framework.mk
index edcae60..f920cbf 100644
--- a/tftf/framework/framework.mk
+++ b/tftf/framework/framework.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018-2024, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2025, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -90,6 +90,7 @@
 ifeq (${ARCH},aarch64)
 # ARMv8.3 Pointer Authentication support files
 FRAMEWORK_SOURCES	+=						\
+	drivers/arm/gic/aarch64/gic_v5.c				\
 	lib/exceptions/aarch64/sync.c					\
 	lib/exceptions/aarch64/serror.c					\
 	lib/extensions/pauth/aarch64/pauth.c				\
diff --git a/tftf/framework/main.c b/tftf/framework/main.c
index 0ae03e5..9543704 100644
--- a/tftf/framework/main.c
+++ b/tftf/framework/main.c
@@ -564,6 +564,8 @@
 	pauth_init_enable();
 #endif /* ENABLE_PAUTH */
 
+	arm_gic_probe();
+
 	tftf_platform_setup();
 	tftf_init_topology();
 
@@ -576,7 +578,7 @@
 	}
 
 	/* Enable the SGI used by the timer management framework */
-	tftf_irq_enable(IRQ_WAKE_SGI, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_WAKE_SGI, GIC_HIGHEST_NS_PRIORITY);
 	enable_irq();
 
 	if (new_test_session()) {
diff --git a/tftf/framework/timer/timer_framework.c b/tftf/framework/timer/timer_framework.c
index 556d46e..0972ceb 100644
--- a/tftf/framework/timer/timer_framework.c
+++ b/tftf/framework/timer/timer_framework.c
@@ -97,7 +97,7 @@
 
 void tftf_initialise_timer_secondary_core(void)
 {
-	if (!IS_SPI(TIMER_IRQ)) {
+	if (!arm_gic_is_irq_shared(TIMER_IRQ)) {
 		arm_gic_set_intr_priority(TIMER_IRQ, GIC_HIGHEST_NS_PRIORITY);
 		arm_gic_intr_enable(TIMER_IRQ);
 	}
@@ -186,7 +186,7 @@
 	if ((!get_current_prog_time()) || (interrupt_req_time[core_pos] <
 				(get_current_prog_time() - TIMER_STEP_VALUE))) {
 
-		if (IS_SPI(TIMER_IRQ)) {
+		if (arm_gic_is_irq_shared(TIMER_IRQ)) {
 			arm_gic_set_intr_target(TIMER_IRQ, core_pos);
 		}
 
@@ -490,7 +490,7 @@
 	 * Also register same handler to IRQ_WAKE_SGI, as it can be waken
 	 * by it.
 	 */
-	ret = tftf_irq_register_handler(IRQ_WAKE_SGI, irq_handler);
+	ret = tftf_irq_register_handler_sgi(IRQ_WAKE_SGI, irq_handler);
 	assert(!ret);
 
 	return ret;
@@ -504,7 +504,7 @@
 	/*
 	 * Unregister the handler for IRQ_WAKE_SGI also
 	 */
-	ret = tftf_irq_unregister_handler(IRQ_WAKE_SGI);
+	ret = tftf_irq_unregister_handler_sgi(IRQ_WAKE_SGI);
 	assert(!ret);
 	/* Validate a handler is registered */
 	assert(timer_handler[core_pos]);
diff --git a/tftf/tests/framework_validation_tests/test_timer_framework.c b/tftf/tests/framework_validation_tests/test_timer_framework.c
index 421ddc6..51b6f9b 100644
--- a/tftf/tests/framework_validation_tests/test_timer_framework.c
+++ b/tftf/tests/framework_validation_tests/test_timer_framework.c
@@ -7,6 +7,7 @@
 #include <arch.h>
 #include <arch_helpers.h>
 #include <debug.h>
+#include <drivers/arm/arm_gic.h>
 #include <events.h>
 #include <irq.h>
 #include <mmio.h>
@@ -48,7 +49,7 @@
 	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
 	unsigned int irq_id = *(unsigned int *) data;
 
-	assert(irq_id == IRQ_WAKE_SGI || irq_id == tftf_get_timer_irq());
+	assert(irq_id == tftf_irq_get_my_sgi_num(IRQ_WAKE_SGI) || irq_id == tftf_get_timer_irq());
 	assert(requested_irq_received[core_pos] == 0);
 
 	if (irq_id == tftf_get_timer_irq()) {
@@ -71,10 +72,10 @@
 	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
 	unsigned int irq_id = *(unsigned int *) data;
 
-	assert(irq_id == IRQ_WAKE_SGI || irq_id == tftf_get_timer_irq());
+	assert(irq_id == tftf_irq_get_my_sgi_num(IRQ_WAKE_SGI) || irq_id == tftf_get_timer_irq());
 	assert(requested_irq_received[core_pos] == 0);
 
-	if (irq_id == IRQ_WAKE_SGI) {
+	if (irq_id == tftf_irq_get_my_sgi_num(IRQ_WAKE_SGI)) {
 		spin_lock(&irq_handler_lock);
 		multiple_timer_count++;
 		spin_unlock(&irq_handler_lock);
diff --git a/tftf/tests/framework_validation_tests/test_validation_irq.c b/tftf/tests/framework_validation_tests/test_validation_irq.c
index 914a1ca..6955191 100644
--- a/tftf/tests/framework_validation_tests/test_validation_irq.c
+++ b/tftf/tests/framework_validation_tests/test_validation_irq.c
@@ -46,13 +46,13 @@
 	counter = 0;
 
 	/* Now register a handler */
-	ret = tftf_irq_register_handler(sgi_id, increment_counter);
+	ret = tftf_irq_register_handler_sgi(sgi_id, increment_counter);
 	if (ret != 0) {
 		tftf_testcase_printf("Failed to register initial IRQ handler\n");
 		return TEST_RESULT_FAIL;
 	}
 
-	tftf_irq_enable(sgi_id, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	/*
 	 * Send the SGI to the calling core and check the IRQ handler has been
@@ -64,6 +64,8 @@
 	while (counter != 1)
 		;
 
+	// TODO all this crap tests tftf itself. It's just slow. Drop?
+	// or add it to some test group that's not built/run by default.
 	/*
 	 * Try to overwrite the IRQ handler. This should fail.
 	 * In debug builds, it would trigger an assertion so we can't test that
@@ -72,7 +74,7 @@
 	 * replace the existing handler and that's something that can be tested.
 	 */
 #if !DEBUG
-	ret = tftf_irq_register_handler(sgi_id, set_counter_to_42);
+	ret = tftf_irq_register_handler_sgi(sgi_id, set_counter_to_42);
 	if (ret == 0) {
 		tftf_testcase_printf(
 			"Overwriting the IRQ handler should have failed\n");
@@ -85,7 +87,7 @@
 		;
 
 	/* Unregister the IRQ handler */
-	ret = tftf_irq_unregister_handler(sgi_id);
+	ret = tftf_irq_unregister_handler_sgi(IRQ_NS_SGI_0);
 	if (ret != 0) {
 		tftf_testcase_printf("Failed to unregister IRQ handler\n");
 		return TEST_RESULT_FAIL;
@@ -116,7 +118,7 @@
 	 * In release builds though, it should just do nothing.
 	 */
 #if !DEBUG
-	ret = tftf_irq_unregister_handler(sgi_id);
+	ret = tftf_irq_unregister_handler_sgi(sgi_id);
 	if (ret == 0) {
 		tftf_testcase_printf(
 			"Unregistering the IRQ handler again should have failed\n");
@@ -124,7 +126,7 @@
 	}
 #endif
 
-	tftf_irq_disable(sgi_id);
+	tftf_irq_disable_sgi(sgi_id);
 
 	return TEST_RESULT_SUCCESS;
 }
diff --git a/tftf/tests/framework_validation_tests/test_validation_sgi.c b/tftf/tests/framework_validation_tests/test_validation_sgi.c
index 0f9ac9b..cafcb7a 100644
--- a/tftf/tests/framework_validation_tests/test_validation_sgi.c
+++ b/tftf/tests/framework_validation_tests/test_validation_sgi.c
@@ -45,17 +45,18 @@
 	unsigned int mpid = read_mpidr_el1();
 	unsigned int core_pos = platform_get_core_pos(mpid);
 	const unsigned int sgi_id = IRQ_NS_SGI_0;
+	const unsigned int sgi_irq = tftf_irq_get_my_sgi_num(sgi_id);
 	test_result_t test_res = TEST_RESULT_SUCCESS;
 	int ret;
 
 	/* Register the local IRQ handler for the SGI */
-	ret = tftf_irq_register_handler(sgi_id, sgi_handler);
+	ret = tftf_irq_register_handler_sgi(sgi_id, sgi_handler);
 	if (ret != 0) {
 		tftf_testcase_printf("Failed to register IRQ %u (%d)",
 				sgi_id, ret);
 		return TEST_RESULT_FAIL;
 	}
-	tftf_irq_enable(sgi_id, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(sgi_id, GIC_HIGHEST_NS_PRIORITY);
 
 	/* Send the SGI to the lead CPU */
 	tftf_send_sgi(sgi_id, core_pos);
@@ -68,14 +69,14 @@
 		continue;
 
 	/* Verify the data received in the SGI handler */
-	if (sgi_data != sgi_id) {
+	if (sgi_data != sgi_irq) {
 		tftf_testcase_printf("Wrong IRQ ID, expected %u, got %u\n",
-			sgi_id, sgi_data);
+			sgi_irq, sgi_data);
 		test_res = TEST_RESULT_FAIL;
 	}
 
-	tftf_irq_disable(sgi_id);
-	tftf_irq_unregister_handler(sgi_id);
+	tftf_irq_disable_sgi(sgi_id);
+	tftf_irq_unregister_handler_sgi(sgi_id);
 
 	return test_res;
 }
diff --git a/tftf/tests/misc_tests/test_pmu_leakage.c b/tftf/tests/misc_tests/test_pmu_leakage.c
index 9b2f735..82b81f4 100644
--- a/tftf/tests/misc_tests/test_pmu_leakage.c
+++ b/tftf/tests/misc_tests/test_pmu_leakage.c
@@ -182,7 +182,7 @@
 	power_state = tftf_make_psci_pstate(MPIDR_AFFLVL0,
 					    PSTATE_TYPE_STANDBY, stateid);
 
-	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	/*
 	 * Mask IRQ to prevent the interrupt handler being invoked
@@ -203,7 +203,7 @@
 	enable_irq();
 	isb();
 
-	tftf_irq_disable(IRQ_NS_SGI_0);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_0);
 
 	return evt_cnt;
 }
@@ -241,7 +241,7 @@
 
 		cnt_sum += evt_cnt;
 
-		tftf_irq_disable(IRQ_NS_SGI_0);
+		tftf_irq_disable_sgi(IRQ_NS_SGI_0);
 	}
 
 	avg_cnt = cnt_sum / ITERATIONS_CNT;
diff --git a/tftf/tests/misc_tests/test_ras_kfh_reflect.c b/tftf/tests/misc_tests/test_ras_kfh_reflect.c
index 7d771a4..9392231 100644
--- a/tftf/tests/misc_tests/test_ras_kfh_reflect.c
+++ b/tftf/tests/misc_tests/test_ras_kfh_reflect.c
@@ -89,12 +89,12 @@
 
 	waitms(50);
 
-	ret = tftf_irq_register_handler(sgi_id, irq_handler);
+	ret = tftf_irq_register_handler_sgi(sgi_id, irq_handler);
 	 if (ret != 0) {
                 tftf_testcase_printf("Failed to register initial IRQ handler\n");
                 return TEST_RESULT_FAIL;
         }
-	tftf_irq_enable(sgi_id, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(sgi_id, GIC_HIGHEST_NS_PRIORITY);
 	tftf_send_sgi(sgi_id, core_pos);
 
 	if ((serror_triggered == false) || (irq_triggered == false)) {
@@ -102,7 +102,7 @@
 		return TEST_RESULT_FAIL;
 	}
 
-	ret = tftf_irq_unregister_handler(sgi_id);
+	ret = tftf_irq_unregister_handler_sgi(sgi_id);
 	if (ret != 0) {
 		tftf_testcase_printf("Failed to unregister IRQ handler\n");
 		return TEST_RESULT_FAIL;
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c b/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c
index 883102e..fb60988 100644
--- a/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_payload_multiple_rec_tests.c
@@ -284,12 +284,12 @@
 	}
 
 destroy_realm:
-	tftf_irq_enable(IRQ_NS_SGI_7, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_7, GIC_HIGHEST_NS_PRIORITY);
 	for (unsigned int i = 1U; i < rec_count; i++) {
 		INFO("Raising NS IRQ for rec %u\n", i);
 		host_rec_send_sgi(&realm, IRQ_NS_SGI_7, i);
 	}
-	tftf_irq_disable(IRQ_NS_SGI_7);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_7);
 	ret2 = host_destroy_realm(&realm);
 	if (!ret1 || !ret2) {
 		ERROR("%s(): enter=%d destroy=%d\n",
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c b/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
index be999c3..015728d 100644
--- a/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
@@ -12,6 +12,7 @@
 #include <irq.h>
 #include <drivers/arm/arm_gic.h>
 #include <drivers/arm/gic_v3.h>
+#include <drivers/arm/gic_v5.h>
 #include <heap/page_alloc.h>
 #include <lib/context_mgmt/context_el1.h>
 #include <lib/context_mgmt/context_el2.h>
@@ -39,6 +40,22 @@
 static uint128_t pauth_keys_after[NUM_KEYS];
 #endif
 
+static unsigned int tftf_get_pmu_irq(void)
+{
+	if (arm_gic_get_version() == 5)
+		return PMU_PPI | INPLACE(INT_TYPE, INT_PPI);
+	else
+		return PMU_PPI;
+}
+
+static unsigned int tftf_get_pmu_virq(void)
+{
+	if (arm_gic_get_version() == 5)
+		return PMU_VIRQ | INPLACE(INT_TYPE, INT_PPI);
+	else
+		return PMU_VIRQ;
+}
+
 /*
  * @Test_Aim@ Test RSI_PLANE_SYSREG_READ/WRITE
  */
@@ -669,8 +686,8 @@
 		u_register_t exit_reason, retrmm;
 		int ret;
 
-		tftf_irq_disable(PMU_PPI);
-		ret = tftf_irq_unregister_handler(PMU_PPI);
+		tftf_irq_disable(tftf_get_pmu_irq());
+		ret = tftf_irq_unregister_handler(tftf_get_pmu_irq());
 		if (ret != 0) {
 			ERROR("Failed to %sregister IRQ handler\n", "un");
 			return false;
@@ -679,10 +696,10 @@
 		/* Inject PMU virtual interrupt */
 		run->entry.gicv3_lrs[0] =
 			ICH_LRn_EL2_STATE_Pending | ICH_LRn_EL2_Group_1 |
-			(PMU_VIRQ << ICH_LRn_EL2_vINTID_SHIFT);
+			(tftf_get_pmu_virq() << ICH_LRn_EL2_vINTID_SHIFT);
 
 		/* Re-enter Realm */
-		INFO("Re-entering Realm with vIRQ %lu pending\n", PMU_VIRQ);
+		INFO("Re-entering Realm with vIRQ %u pending\n", tftf_get_pmu_virq());
 
 		retrmm = host_realm_rec_enter(realm_ptr, &exit_reason,
 						&host_call_result, rec_num);
@@ -810,17 +827,17 @@
 	test_result_t ret;
 
 	/* Register PMU IRQ handler */
-	if (tftf_irq_register_handler(PMU_PPI, host_overflow_interrupt) != 0) {
+	if (tftf_irq_register_handler(tftf_get_pmu_irq(), host_overflow_interrupt) != 0) {
 		tftf_testcase_printf("Failed to %sregister IRQ handler\n", "");
 		return TEST_RESULT_FAIL;
 	}
 
-	tftf_irq_enable(PMU_PPI, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable(tftf_get_pmu_irq(), GIC_HIGHEST_NS_PRIORITY);
 
 	ret = host_test_realm_pmuv3(cmd);
 	if (ret != TEST_RESULT_SUCCESS) {
-		tftf_irq_disable(PMU_PPI);
-		if (tftf_irq_unregister_handler(PMU_PPI) != 0) {
+		tftf_irq_disable(tftf_get_pmu_irq());
+		if (tftf_irq_unregister_handler(tftf_get_pmu_irq()) != 0) {
 			ERROR("Failed to %sregister IRQ handler\n", "un");
 			return TEST_RESULT_FAIL;
 		}
diff --git a/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c b/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c
index 2697fac..6e7f3f7 100644
--- a/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c
+++ b/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c
@@ -354,7 +354,7 @@
 	 * Enable reception of SGI 0 on the calling CPU.
 	 * SGI 0 will serve as the wake-up event to come out of suspend.
 	 */
-	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	expected_return_val = tftf_psci_make_composite_state_id(
 			PSTATE_AFF_LVL_0, PSTATE_TYPE_POWERDOWN, &stateid);
@@ -377,7 +377,7 @@
 
 	psci_ret = tftf_cpu_suspend(power_state);
 
-	tftf_irq_disable(IRQ_NS_SGI_0);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_0);
 
 	if (psci_ret != PSCI_E_SUCCESS) {
 		tftf_testcase_printf("Failed to suspend (%i)\n", psci_ret);
diff --git a/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c b/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c
index e802d43..ba67ab1 100644
--- a/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c
+++ b/tftf/tests/runtime_services/standard_service/psci/api_tests/cpu_suspend/test_suspend.c
@@ -49,7 +49,7 @@
 	unsigned int irq_id = *(unsigned int *) data;
 #endif
 
-	assert(irq_id == IRQ_WAKE_SGI || irq_id == tftf_get_timer_irq());
+	assert(irq_id == tftf_irq_get_my_sgi_num(IRQ_WAKE_SGI) || irq_id == tftf_get_timer_irq());
 	assert(requested_irq_received[core_pos] == 0);
 
 	requested_irq_received[core_pos] = 1;
diff --git a/tftf/tests/runtime_services/standard_service/psci/api_tests/system_suspend/test_psci_system_suspend.c b/tftf/tests/runtime_services/standard_service/psci/api_tests/system_suspend/test_psci_system_suspend.c
index e79293b..4563bcb 100644
--- a/tftf/tests/runtime_services/standard_service/psci/api_tests/system_suspend/test_psci_system_suspend.c
+++ b/tftf/tests/runtime_services/standard_service/psci/api_tests/system_suspend/test_psci_system_suspend.c
@@ -323,14 +323,14 @@
 	int sgi_ret;
 
 	/* Register the local IRQ handler for the SGI */
-	sgi_ret = tftf_irq_register_handler(sgi_id, sgi_handler);
+	sgi_ret = tftf_irq_register_handler_sgi(sgi_id, sgi_handler);
 	if (sgi_ret != 0) {
 		tftf_testcase_printf("Failed to register IRQ %u (%d)",
 				sgi_id, sgi_ret);
 		return TEST_RESULT_FAIL;
 	}
 	/* Enable SGI */
-	tftf_irq_enable(sgi_id, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(sgi_id, GIC_HIGHEST_NS_PRIORITY);
 
 	/* Signal to the lead CPU that we are ready to receive SGI */
 	tftf_send_event(&cpu_ready[core_pos]);
@@ -342,8 +342,8 @@
 	tftf_send_event(&sgi_received[core_pos]);
 
 	/* Unregister SGI handler */
-	tftf_irq_disable(sgi_id);
-	tftf_irq_unregister_handler(sgi_id);
+	tftf_irq_disable_sgi(sgi_id);
+	tftf_irq_unregister_handler_sgi(sgi_id);
 	return TEST_RESULT_SUCCESS;
 }
 
@@ -458,6 +458,7 @@
 {
 	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
 	const unsigned int sgi_id = IRQ_NS_SGI_0;
+	const unsigned int sgi_irq = tftf_irq_get_my_sgi_num(sgi_id);
 	int sgi_ret;
 	int psci_ret;
 	test_result_t ret = TEST_RESULT_SUCCESS;
@@ -473,7 +474,7 @@
 	wakeup_irq_rcvd[core_pos] = 0;
 
 	/* Register the local IRQ handler for the SGI */
-	sgi_ret = tftf_irq_register_handler(sgi_id, sgi_handler);
+	sgi_ret = tftf_irq_register_handler_sgi(sgi_id, sgi_handler);
 	if (sgi_ret != 0) {
 		tftf_testcase_printf("Failed to register IRQ %u (%d)",
 				sgi_id, sgi_ret);
@@ -489,7 +490,7 @@
 	 */
 	tftf_program_timer(SUSPEND_TIME_3_SECS);
 
-	tftf_irq_enable(sgi_id, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(sgi_id, GIC_HIGHEST_NS_PRIORITY);
 	disable_irq();
 
 	/* Send the SGI to the lead CPU */
@@ -517,9 +518,9 @@
 		;
 
 	/* Verify the sgi data received by the SGI handler */
-	if (sgi_data != sgi_id) {
+	if (sgi_data != sgi_irq) {
 		tftf_testcase_printf("Wrong IRQ ID, expected %u, got %u\n",
-				sgi_id, sgi_data);
+				sgi_irq, sgi_data);
 		ret = TEST_RESULT_FAIL;
 	}
 
@@ -531,8 +532,8 @@
 	tftf_cancel_timer();
 
 	/* Unregister SGI handler */
-	tftf_irq_disable(sgi_id);
-	tftf_irq_unregister_handler(sgi_id);
+	tftf_irq_disable_sgi(sgi_id);
+	tftf_irq_unregister_handler_sgi(sgi_id);
 
 	return ret;
 }
@@ -662,13 +663,13 @@
 	unsigned int core_pos = platform_get_core_pos(mpid);
 	int ret;
 
-	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	/* Tell the lead CPU that the calling CPU is about to suspend itself */
 	tftf_send_event(&cpu_ready[core_pos]);
 
 	ret = tftf_cpu_suspend(deepest_power_state);
-	tftf_irq_disable(IRQ_NS_SGI_0);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_0);
 
 	if (ret) {
 		ERROR(" CPU suspend failed with error %x\n", ret);
diff --git a/tftf/tests/runtime_services/standard_service/psci/api_tests/validate_power_state/test_validate_power_state.c b/tftf/tests/runtime_services/standard_service/psci/api_tests/validate_power_state/test_validate_power_state.c
index 565914b..ac3b338 100644
--- a/tftf/tests/runtime_services/standard_service/psci/api_tests/validate_power_state/test_validate_power_state.c
+++ b/tftf/tests/runtime_services/standard_service/psci/api_tests/validate_power_state/test_validate_power_state.c
@@ -408,8 +408,8 @@
 	test_result_t ret;
 	unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
 
-	tftf_irq_register_handler(IRQ_NS_SGI_0, validate_pstate_sgi_handler);
-	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_register_handler_sgi(IRQ_NS_SGI_0, validate_pstate_sgi_handler);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	/*
 	 * Mask IRQ to prevent the interrupt handler being invoked
@@ -428,8 +428,8 @@
 	while (!sgi_received[core_pos])
 		;
 
-	tftf_irq_disable(IRQ_NS_SGI_0);
-	tftf_irq_unregister_handler(IRQ_NS_SGI_0);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_0);
+	tftf_irq_unregister_handler_sgi(IRQ_NS_SGI_0);
 
 	return ret;
 }
diff --git a/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c
index a553adf..6a8c0e0 100644
--- a/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c
+++ b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2024-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -8,6 +8,7 @@
 #include <tftf_lib.h>
 #include <sdei.h>
 #include <drivers/arm/arm_gic.h>
+#include <drivers/arm/gic_v2v3_common.h>
 
 /*
  * Only this many events can be bound in the PPI range. If you attempt to bind
diff --git a/tftf/tests/runtime_services/trusted_os/tsp/test_irq_preempted_std_smc.c b/tftf/tests/runtime_services/trusted_os/tsp/test_irq_preempted_std_smc.c
index 3e24ccd..3af35ad 100644
--- a/tftf/tests/runtime_services/trusted_os/tsp/test_irq_preempted_std_smc.c
+++ b/tftf/tests/runtime_services/trusted_os/tsp/test_irq_preempted_std_smc.c
@@ -66,7 +66,7 @@
 {
 	/* SGIs #0 - #6 are freely available. */
 
-	int ret = tftf_irq_register_handler(IRQ_NS_SGI_0, test_handler);
+	int ret = tftf_irq_register_handler_sgi(IRQ_NS_SGI_0, test_handler);
 
 	if (ret != 0) {
 		tftf_testcase_printf(
@@ -75,7 +75,7 @@
 		return -1;
 	}
 
-	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	return 0;
 }
@@ -83,9 +83,9 @@
 /* Disable and unregister the dummy handler for SGI #0. */
 static void unregister_and_disable_test_sgi_handler(void)
 {
-	tftf_irq_disable(IRQ_NS_SGI_0);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_0);
 
-	tftf_irq_unregister_handler(IRQ_NS_SGI_0);
+	tftf_irq_unregister_handler_sgi(IRQ_NS_SGI_0);
 }
 
 /*
diff --git a/tftf/tests/runtime_services/trusted_os/tsp/test_normal_int_switch.c b/tftf/tests/runtime_services/trusted_os/tsp/test_normal_int_switch.c
index eb04006..d038f86 100644
--- a/tftf/tests/runtime_services/trusted_os/tsp/test_normal_int_switch.c
+++ b/tftf/tests/runtime_services/trusted_os/tsp/test_normal_int_switch.c
@@ -64,7 +64,7 @@
 		shared_data.wait_for_fiq = 1;
 
 	/* Register Handler for the interrupt. SGIs #0 - #6 are available. */
-	rc = tftf_irq_register_handler(IRQ_NS_SGI_0, sgi_handler);
+	rc = tftf_irq_register_handler_sgi(IRQ_NS_SGI_0, sgi_handler);
 	if (rc != 0) {
 		tftf_testcase_printf("Failed to register SGI handler. "
 				"Error code = %d\n", rc);
@@ -72,7 +72,7 @@
 	}
 
 	/* Enable SGI #0 */
-	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+	tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
 
 	/* Set PSTATE.I to 0. */
 	disable_irq();
@@ -99,10 +99,10 @@
 	enable_irq();
 
 	/* Disable SGI #0 */
-	tftf_irq_disable(IRQ_NS_SGI_0);
+	tftf_irq_disable_sgi(IRQ_NS_SGI_0);
 
 	/* Unregister handler */
-	rc = tftf_irq_unregister_handler(IRQ_NS_SGI_0);
+	rc = tftf_irq_unregister_handler_sgi(IRQ_NS_SGI_0);
 	if (rc != 0) {
 		tftf_testcase_printf("Failed to unregister IRQ handler. "
 				     "Error code = %d\n", rc);