feat(versal-net): add support for platform management

Add support for PM EEMI interface for Versal_net. Also use PM
APIs in psci ops. Added TFA_NO_PM flag to disable PM functionality.

Signed-off-by: Jay Buddhabhatti <jay.buddhabhatti@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@amd.com>
Change-Id: If2b2941c868bc9b0850d7f3adb81eac0e660c149
diff --git a/plat/xilinx/versal_net/aarch64/versal_net_common.c b/plat/xilinx/versal_net/aarch64/versal_net_common.c
index 46ebc3e..91d0371 100644
--- a/plat/xilinx/versal_net/aarch64/versal_net_common.c
+++ b/plat/xilinx/versal_net/aarch64/versal_net_common.c
@@ -110,6 +110,11 @@
 		      VERSAL_NET_IOU_SCNTRS_CONTROL_EN);
 
 	generic_delay_timer_init();
+
+#if (TFA_NO_PM == 0)
+	/* Configure IPI data for versal_net */
+	versal_net_ipi_config_table_init();
+#endif
 }
 
 uint32_t plat_get_syscnt_freq2(void)
diff --git a/plat/xilinx/versal_net/bl31_versal_net_setup.c b/plat/xilinx/versal_net/bl31_versal_net_setup.c
index ba23eb9..97080e9 100644
--- a/plat/xilinx/versal_net/bl31_versal_net_setup.c
+++ b/plat/xilinx/versal_net/bl31_versal_net_setup.c
@@ -131,6 +131,55 @@
 	NOTICE("BL31: Non secure code at 0x%lx\n", bl33_image_ep_info.pc);
 }
 
+static versal_intr_info_type_el3_t type_el3_interrupt_table[MAX_INTR_EL3];
+
+int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler)
+{
+	static uint32_t index;
+	uint32_t i;
+
+	/* Validate 'handler' and 'id' parameters */
+	if (handler == NULL || index >= MAX_INTR_EL3) {
+		return -EINVAL;
+	}
+
+	/* Check if a handler has already been registered */
+	for (i = 0; i < index; i++) {
+		if (id == type_el3_interrupt_table[i].id) {
+			return -EALREADY;
+		}
+	}
+
+	type_el3_interrupt_table[index].id = id;
+	type_el3_interrupt_table[index].handler = handler;
+
+	index++;
+
+	return 0;
+}
+
+static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags,
+					  void *handle, void *cookie)
+{
+	uint32_t intr_id;
+	uint32_t i;
+	interrupt_type_handler_t handler = NULL;
+
+	intr_id = plat_ic_get_pending_interrupt_id();
+
+	for (i = 0; i < MAX_INTR_EL3; i++) {
+		if (intr_id == type_el3_interrupt_table[i].id) {
+			handler = type_el3_interrupt_table[i].handler;
+		}
+	}
+
+	if (handler != NULL) {
+		handler(intr_id, flags, handle, cookie);
+	}
+
+	return 0;
+}
+
 void bl31_platform_setup(void)
 {
 	/* Initialize the gic cpu and distributor interfaces */
@@ -140,6 +189,15 @@
 
 void bl31_plat_runtime_setup(void)
 {
+	uint64_t flags = 0;
+	int32_t rc;
+
+	set_interrupt_rm_flag(flags, NON_SECURE);
+	rc = register_interrupt_type_handler(INTR_TYPE_EL3,
+					     rdo_el3_interrupt_handler, flags);
+	if (rc != 0) {
+		panic();
+	}
 }
 
 /*
diff --git a/plat/xilinx/versal_net/include/plat_private.h b/plat/xilinx/versal_net/include/plat_private.h
index 6bd01ba..6a3bc19 100644
--- a/plat/xilinx/versal_net/include/plat_private.h
+++ b/plat/xilinx/versal_net/include/plat_private.h
@@ -9,8 +9,14 @@
 #ifndef PLAT_PRIVATE_H
 #define PLAT_PRIVATE_H
 
+#include <bl31/interrupt_mgmt.h>
 #include <lib/xlat_tables/xlat_tables_v2.h>
 
+typedef struct versal_intr_info_type_el3 {
+	uint32_t id;
+	interrupt_type_handler_t handler;
+} versal_intr_info_type_el3_t;
+
 void versal_net_config_setup(void);
 
 const mmap_region_t *plat_versal_net_get_mmap(void);
@@ -18,7 +24,12 @@
 void plat_versal_net_gic_driver_init(void);
 void plat_versal_net_gic_init(void);
 void plat_versal_net_gic_cpuif_enable(void);
+void plat_versal_net_gic_cpuif_disable(void);
 void plat_versal_net_gic_pcpu_init(void);
+void plat_versal_net_gic_save(void);
+void plat_versal_net_gic_resume(void);
+void plat_versal_net_gic_redistif_on(void);
+void plat_versal_net_gic_redistif_off(void);
 
 extern uint32_t cpu_clock, platform_id, platform_version;
 void board_detection(void);
@@ -26,6 +37,11 @@
 uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3,
 		       uint64_t x4, void *cookie, void *handle, uint64_t flags);
 int32_t sip_svc_setup_init(void);
+/*
+ * Register handler to specific GIC entrance
+ * for INTR_TYPE_EL3 type of interrupt
+ */
+int request_intr_type_el3(uint32_t irq, interrupt_type_handler_t fiq_handler);
 
 #define PM_GET_CHIPID			(24U)
 #define IOCTL_OSPI_MUX_SELECT		(21U)
diff --git a/plat/xilinx/versal_net/include/versal_net_def.h b/plat/xilinx/versal_net/include/versal_net_def.h
index 9b80683..649886b 100644
--- a/plat/xilinx/versal_net/include/versal_net_def.h
+++ b/plat/xilinx/versal_net/include/versal_net_def.h
@@ -12,6 +12,7 @@
 #include <plat/arm/common/smccc_def.h>
 #include <plat/common/common_def.h>
 
+#define MAX_INTR_EL3			2
 /* This part is taken from U-Boot project under GPL that's why dual license above */
 #define __bf_shf(x) (__builtin_ffsll(x) - 1U)
 #define FIELD_GET(_mask, _reg)						\
@@ -65,6 +66,16 @@
 /* Firmware Image Package */
 #define VERSAL_NET_PRIMARY_CPU		U(0)
 
+#define CORE_0_IEN_POWER_OFFSET			(0x00000018U)
+#define APU_PCIL_CORE_X_IEN_POWER_REG(cpu_id)	(APU_PCLI + (CORE_0_IEN_POWER_OFFSET + \
+						 (0x30 * cpu_id)))
+#define APU_PCIL_CORE_X_IEN_POWER_MASK		(0x00000001U)
+#define CORE_0_IDS_POWER_OFFSET			(0x0000001CU)
+#define APU_PCIL_CORE_X_IDS_POWER_REG(cpu_id)	(APU_PCLI + (CORE_0_IDS_POWER_OFFSET + \
+						 (0x30 * cpu_id)))
+#define APU_PCIL_CORE_X_IDS_POWER_MASK		(0x00000001U)
+#define CORE_PWRDN_EN_BIT_MASK			(0x1U)
+
 /*******************************************************************************
  * memory map related constants
  ******************************************************************************/
@@ -118,4 +129,42 @@
 #define PLAT_VERSAL_NET_CRASH_UART_CLK_IN_HZ	VERSAL_NET_UART_CLOCK
 #define VERSAL_NET_CONSOLE_BAUDRATE		VERSAL_NET_UART_BAUDRATE
 
+/*******************************************************************************
+ * IPI registers and bitfields
+ ******************************************************************************/
+#define IPI0_REG_BASE		(0xEB330000U)
+#define IPI0_TRIG_BIT		(1 << 2)
+#define PMC_IPI_TRIG_BIT	(1 << 1)
+#define IPI1_REG_BASE		(0xEB340000U)
+#define IPI1_TRIG_BIT		(1 << 3)
+#define IPI2_REG_BASE		(0xEB350000U)
+#define IPI2_TRIG_BIT		(1 << 4)
+#define IPI3_REG_BASE		(0xEB360000U)
+#define IPI3_TRIG_BIT		(1 << 5)
+#define IPI4_REG_BASE		(0xEB370000U)
+#define IPI4_TRIG_BIT		(1 << 6)
+#define IPI5_REG_BASE		(0xEB380000U)
+#define IPI5_TRIG_BIT		(1 << 7)
+
+/* Processor core device IDs */
+#define PM_DEV_CLUSTER0_ACPU_0	(0x1810C0AFU)
+#define PM_DEV_CLUSTER0_ACPU_1	(0x1810C0B0U)
+#define PM_DEV_CLUSTER0_ACPU_2	(0x1810C0B1U)
+#define PM_DEV_CLUSTER0_ACPU_3	(0x1810C0B2U)
+
+#define PM_DEV_CLUSTER1_ACPU_0	(0x1810C0B3U)
+#define PM_DEV_CLUSTER1_ACPU_1	(0x1810C0B4U)
+#define PM_DEV_CLUSTER1_ACPU_2	(0x1810C0B5U)
+#define PM_DEV_CLUSTER1_ACPU_3	(0x1810C0B6U)
+
+#define PM_DEV_CLUSTER2_ACPU_0	(0x1810C0B7U)
+#define PM_DEV_CLUSTER2_ACPU_1	(0x1810C0B8U)
+#define PM_DEV_CLUSTER2_ACPU_2	(0x1810C0B9U)
+#define PM_DEV_CLUSTER2_ACPU_3	(0x1810C0BAU)
+
+#define PM_DEV_CLUSTER3_ACPU_0	(0x1810C0BBU)
+#define PM_DEV_CLUSTER3_ACPU_1	(0x1810C0BCU)
+#define PM_DEV_CLUSTER3_ACPU_2	(0x1810C0BDU)
+#define PM_DEV_CLUSTER3_ACPU_3	(0x1810C0BEU)
+
 #endif /* VERSAL_NET_DEF_H */
diff --git a/plat/xilinx/versal_net/plat_psci_pm.c b/plat/xilinx/versal_net/plat_psci_pm.c
new file mode 100644
index 0000000..8beaa9a
--- /dev/null
+++ b/plat/xilinx/versal_net/plat_psci_pm.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2022, Xilinx, Inc. All rights reserved.
+ * Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <common/debug.h>
+#include <lib/mmio.h>
+#include <lib/psci/psci.h>
+#include <plat/arm/common/plat_arm.h>
+#include <plat/common/platform.h>
+#include <plat_arm.h>
+
+#include <plat_private.h>
+#include "pm_api_sys.h"
+#include "pm_client.h"
+#include <pm_common.h>
+#include "pm_svc_main.h"
+#include "versal_net_def.h"
+
+static uintptr_t versal_net_sec_entry;
+
+static int32_t versal_net_pwr_domain_on(u_register_t mpidr)
+{
+	uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
+	const struct pm_proc *proc;
+
+	VERBOSE("%s: mpidr: 0x%lx, cpuid: %x\n",
+		__func__, mpidr, cpu_id);
+
+	if (cpu_id == -1) {
+		return PSCI_E_INTERN_FAIL;
+	}
+
+	proc = pm_get_proc(cpu_id);
+	if (!proc) {
+		return PSCI_E_INTERN_FAIL;
+	}
+
+	pm_req_wakeup(proc->node_id, (versal_net_sec_entry & 0xFFFFFFFFU) | 0x1U,
+		      versal_net_sec_entry >> 32, 0, 0);
+
+	/* Clear power down request */
+	pm_client_wakeup(proc);
+
+	return PSCI_E_SUCCESS;
+}
+
+/**
+ * versal_net_pwr_domain_off() - This function performs actions to turn off core
+ *
+ * @param target_state	Targeted state
+ */
+static void versal_net_pwr_domain_off(const psci_power_state_t *target_state)
+{
+	uint32_t cpu_id = plat_my_core_pos();
+	const struct pm_proc *proc = pm_get_proc(cpu_id);
+
+	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
+		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
+			__func__, i, target_state->pwr_domain_state[i]);
+	}
+
+	/* Prevent interrupts from spuriously waking up this cpu */
+	plat_versal_net_gic_cpuif_disable();
+
+	/*
+	 * Send request to PMC to power down the appropriate APU CPU
+	 * core.
+	 * According to PSCI specification, CPU_off function does not
+	 * have resume address and CPU core can only be woken up
+	 * invoking CPU_on function, during which resume address will
+	 * be set.
+	 */
+	pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0,
+			SECURE_FLAG);
+}
+
+/**
+ * versal_net_system_reset() - This function sends the reset request
+ * to firmware for the system to reset.  This function does not return.
+ */
+static void __dead2 versal_net_system_reset(void)
+{
+	/* Send the system reset request to the PMC */
+	pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET,
+			  pm_get_shutdown_scope(), SECURE_FLAG);
+
+	while (1) {
+		wfi();
+	}
+}
+
+/**
+ * versal_net_pwr_domain_suspend() - This function sends request to PMC to suspend
+ * core.
+ *
+ * @param target_state	Targeted state
+ */
+static void versal_net_pwr_domain_suspend(const psci_power_state_t *target_state)
+{
+	uint32_t state;
+	uint32_t cpu_id = plat_my_core_pos();
+	const struct pm_proc *proc = pm_get_proc(cpu_id);
+
+	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
+		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
+			__func__, i, target_state->pwr_domain_state[i]);
+	}
+
+	plat_versal_net_gic_cpuif_disable();
+
+	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
+		plat_versal_net_gic_save();
+	}
+
+	state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
+		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
+
+	/* Send request to PMC to suspend this core */
+	pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_net_sec_entry,
+			SECURE_FLAG);
+
+	/* TODO: disable coherency */
+}
+
+static void versal_net_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+	(void)target_state;
+
+	/* Enable the gic cpu interface */
+	plat_versal_net_gic_pcpu_init();
+
+	/* Program the gic per-cpu distributor or re-distributor interface */
+	plat_versal_net_gic_cpuif_enable();
+}
+
+/**
+ * versal_net_pwr_domain_suspend_finish() - This function performs actions to finish
+ * suspend procedure.
+ *
+ * @param target_state	Targeted state
+ */
+static void versal_net_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
+{
+	uint32_t cpu_id = plat_my_core_pos();
+	const struct pm_proc *proc = pm_get_proc(cpu_id);
+
+	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
+		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
+			__func__, i, target_state->pwr_domain_state[i]);
+
+	/* Clear the APU power control register for this cpu */
+	pm_client_wakeup(proc);
+
+	/* TODO: enable coherency */
+
+	/* APU was turned off, so restore GIC context */
+	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
+		plat_versal_net_gic_resume();
+	}
+
+	plat_versal_net_gic_cpuif_enable();
+}
+
+/**
+ * versal_net_system_off() - This function sends the system off request
+ * to firmware.  This function does not return.
+ */
+static void __dead2 versal_net_system_off(void)
+{
+	/* Send the power down request to the PMC */
+	pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN,
+			  pm_get_shutdown_scope(), SECURE_FLAG);
+
+	while (1) {
+		wfi();
+	}
+}
+
+/**
+ * versal_net_validate_power_state() - This function ensures that the power state
+ * parameter in request is valid.
+ *
+ * @param power_state		Power state of core
+ * @param req_state		Requested state
+ *
+ * @return Returns status, either PSCI_E_SUCCESS or reason
+ */
+static int32_t versal_net_validate_power_state(unsigned int power_state,
+					       psci_power_state_t *req_state)
+{
+	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
+
+	int32_t pstate = psci_get_pstate_type(power_state);
+
+	assert(req_state);
+
+	/* Sanity check the requested state */
+	if (pstate == PSTATE_TYPE_STANDBY) {
+		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
+	} else {
+		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
+	}
+
+	/* We expect the 'state id' to be zero */
+	if (psci_get_pstate_id(power_state)) {
+		return PSCI_E_INVALID_PARAMS;
+	}
+
+	return PSCI_E_SUCCESS;
+}
+
+/**
+ * versal_net_get_sys_suspend_power_state() - Get power state for system suspend
+ *
+ * @param req_state	Requested state
+ */
+static void versal_net_get_sys_suspend_power_state(psci_power_state_t *req_state)
+{
+	req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
+	req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
+}
+
+static const struct plat_psci_ops versal_net_nopmc_psci_ops = {
+	.pwr_domain_on                  = versal_net_pwr_domain_on,
+	.pwr_domain_off                 = versal_net_pwr_domain_off,
+	.pwr_domain_on_finish           = versal_net_pwr_domain_on_finish,
+	.pwr_domain_suspend             = versal_net_pwr_domain_suspend,
+	.pwr_domain_suspend_finish      = versal_net_pwr_domain_suspend_finish,
+	.system_off                     = versal_net_system_off,
+	.system_reset                   = versal_net_system_reset,
+	.validate_power_state           = versal_net_validate_power_state,
+	.get_sys_suspend_power_state    = versal_net_get_sys_suspend_power_state,
+};
+
+/*******************************************************************************
+ * Export the platform specific power ops.
+ ******************************************************************************/
+int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint,
+			    const struct plat_psci_ops **psci_ops)
+{
+	versal_net_sec_entry = sec_entrypoint;
+
+	VERBOSE("Setting up entry point %lx\n", versal_net_sec_entry);
+
+	*psci_ops = &versal_net_nopmc_psci_ops;
+
+	return 0;
+}
+
+int32_t sip_svc_setup_init(void)
+{
+	return pm_setup();
+}
+
+uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4,
+		     void *cookie, void *handle, uint64_t flags)
+{
+	return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags);
+}
diff --git a/plat/xilinx/versal_net/platform.mk b/plat/xilinx/versal_net/platform.mk
index f361dfe..08e65ac 100644
--- a/plat/xilinx/versal_net/platform.mk
+++ b/plat/xilinx/versal_net/platform.mk
@@ -13,9 +13,14 @@
 PL011_GENERIC_UART := 1
 GIC_ENABLE_V4_EXTN :=  0
 GICV3_SUPPORT_GIC600 := 1
+TFA_NO_PM := 0
 
 override CTX_INCLUDE_AARCH32_REGS    := 0
 
+ifdef TFA_NO_PM
+   $(eval $(call add_define,TFA_NO_PM))
+endif
+
 ifdef VERSAL_NET_ATF_MEM_BASE
     $(eval $(call add_define,VERSAL_NET_ATF_MEM_BASE))
 
@@ -68,9 +73,18 @@
 BL31_SOURCES		+=	drivers/arm/cci/cci.c				\
 				lib/cpus/aarch64/cortex_a78_ae.S		\
 				lib/cpus/aarch64/cortex_a78.S			\
-				plat/common/plat_psci_common.c			\
-				${PLAT_PATH}/plat_psci.c			\
-				plat/xilinx/common/plat_startup.c		\
+				plat/common/plat_psci_common.c
+ifeq ($(TFA_NO_PM), 0)
+BL31_SOURCES		+=	plat/xilinx/versal/pm_service/pm_api_sys.c	\
+				plat/xilinx/common/pm_service/pm_ipi.c		\
+				${PLAT_PATH}/plat_psci_pm.c			\
+				plat/xilinx/versal/pm_service/pm_svc_main.c	\
+				${PLAT_PATH}/pm_service/pm_client.c		\
+				${PLAT_PATH}/versal_net_ipi.c
+else
+BL31_SOURCES		+=	${PLAT_PATH}/plat_psci.c
+endif
+BL31_SOURCES		+=	plat/xilinx/common/plat_startup.c		\
 				plat/xilinx/common/ipi.c			\
 				plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \
 				${PLAT_PATH}/bl31_versal_net_setup.c		\
diff --git a/plat/xilinx/versal_net/pm_service/pm_client.c b/plat/xilinx/versal_net/pm_service/pm_client.c
new file mode 100644
index 0000000..6487324
--- /dev/null
+++ b/plat/xilinx/versal_net/pm_service/pm_client.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2022, Xilinx, Inc. All rights reserved.
+ * Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * APU specific definition of processors in the subsystem as well as functions
+ * for getting information about and changing state of the APU.
+ */
+
+#include <assert.h>
+
+#include <drivers/arm/gic_common.h>
+#include <drivers/arm/gicv3.h>
+#include <lib/bakery_lock.h>
+#include <lib/mmio.h>
+#include <lib/mmio.h>
+#include <lib/utils.h>
+#include <plat/common/platform.h>
+
+#include <plat_ipi.h>
+#include <platform_def.h>
+#include "pm_api_sys.h"
+#include "pm_client.h"
+#include <versal_net_def.h>
+
+#define UNDEFINED_CPUID		(~0)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(cpu_pwrctrl_val, S3_0_C15_C2_7)
+DEFINE_BAKERY_LOCK(pm_client_secure_lock);
+
+static const struct pm_ipi apu_ipi = {
+	.local_ipi_id = IPI_ID_APU,
+	.remote_ipi_id = IPI_ID_PMC,
+	.buffer_base = IPI_BUFFER_APU_BASE,
+};
+
+/* Order in pm_procs_all array must match cpu ids */
+static const struct pm_proc pm_procs_all[] = {
+	{
+		.node_id = PM_DEV_CLUSTER0_ACPU_0,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER0_ACPU_1,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER0_ACPU_2,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER0_ACPU_3,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER1_ACPU_0,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER1_ACPU_1,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER1_ACPU_2,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER1_ACPU_3,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER2_ACPU_0,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER2_ACPU_1,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER2_ACPU_2,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER2_ACPU_3,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER3_ACPU_0,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER3_ACPU_1,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER3_ACPU_2,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	},
+	{
+		.node_id = PM_DEV_CLUSTER3_ACPU_3,
+		.ipi = &apu_ipi,
+		.pwrdn_mask = 0,
+	}
+};
+
+const struct pm_proc *primary_proc = &pm_procs_all[0];
+
+/**
+ * pm_get_proc() - returns pointer to the proc structure
+ * @param cpuid	id of the cpu whose proc struct pointer should be returned
+ *
+ * @return pointer to a proc structure if proc is found, otherwise NULL
+ */
+const struct pm_proc *pm_get_proc(uint32_t cpuid)
+{
+	if (cpuid < ARRAY_SIZE(pm_procs_all)) {
+		return &pm_procs_all[cpuid];
+	}
+
+	NOTICE("ERROR: cpuid: %d proc NULL\n", cpuid);
+	return NULL;
+}
+
+/**
+ * pm_client_suspend() - Client-specific suspend actions
+ *
+ * This function should contain any PU-specific actions
+ * required prior to sending suspend request to PMU
+ * Actions taken depend on the state system is suspending to.
+ *
+ * @param proc	processor which need to suspend
+ * @param state	desired suspend state
+ */
+void pm_client_suspend(const struct pm_proc *proc, uint32_t state)
+{
+	uint32_t cpu_id = plat_my_core_pos();
+	uintptr_t val;
+
+	bakery_lock_get(&pm_client_secure_lock);
+
+	/* TODO: Set wakeup source */
+
+	val = read_cpu_pwrctrl_val();
+	val |= CORE_PWRDN_EN_BIT_MASK;
+	write_cpu_pwrctrl_val(val);
+
+	isb();
+
+	mmio_write_32(APU_PCIL_CORE_X_IEN_POWER_REG(cpu_id),
+		      APU_PCIL_CORE_X_IEN_POWER_MASK);
+
+	bakery_lock_release(&pm_client_secure_lock);
+}
+
+/**
+ * pm_get_cpuid() - get the local cpu ID for a global node ID
+ * @param nid	node id of the processor
+ *
+ * @return the cpu ID (starting from 0) for the subsystem
+ */
+static uint32_t pm_get_cpuid(uint32_t nid)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
+		if (pm_procs_all[i].node_id == nid) {
+			return i;
+		}
+	}
+	return UNDEFINED_CPUID;
+}
+
+/**
+ * pm_client_wakeup() - Client-specific wakeup actions
+ *
+ * This function should contain any PU-specific actions
+ * required for waking up another APU core
+ *
+ * @param proc	Processor which need to wakeup
+ */
+void pm_client_wakeup(const struct pm_proc *proc)
+{
+	uint32_t cpuid = pm_get_cpuid(proc->node_id);
+
+	if (cpuid == UNDEFINED_CPUID) {
+		return;
+	}
+
+	bakery_lock_get(&pm_client_secure_lock);
+
+	/* TODO: clear powerdown bit for affected cpu */
+
+	bakery_lock_release(&pm_client_secure_lock);
+}
+
+/**
+ * pm_client_abort_suspend() - Client-specific abort-suspend actions
+ *
+ * This function should contain any PU-specific actions
+ * required for aborting a prior suspend request
+ */
+void pm_client_abort_suspend(void)
+{
+	uint32_t cpu_id = plat_my_core_pos();
+	uintptr_t val;
+
+	/* Enable interrupts at processor level (for current cpu) */
+	gicv3_cpuif_enable(plat_my_core_pos());
+
+	bakery_lock_get(&pm_client_secure_lock);
+
+	/* Clear powerdown request */
+	val = read_cpu_pwrctrl_val();
+	val &= ~CORE_PWRDN_EN_BIT_MASK;
+	write_cpu_pwrctrl_val(val);
+
+	isb();
+
+	/* Disabled power down interrupt */
+	mmio_write_32(APU_PCIL_CORE_X_IDS_POWER_REG(cpu_id),
+			APU_PCIL_CORE_X_IDS_POWER_MASK);
+
+	bakery_lock_release(&pm_client_secure_lock);
+}
diff --git a/plat/xilinx/versal_net/versal_net_gicv3.c b/plat/xilinx/versal_net/versal_net_gicv3.c
index 028c187..b7ac6ab 100644
--- a/plat/xilinx/versal_net/versal_net_gicv3.c
+++ b/plat/xilinx/versal_net/versal_net_gicv3.c
@@ -22,7 +22,10 @@
 #pragma weak plat_versal_net_gic_driver_init
 #pragma weak plat_versal_net_gic_init
 #pragma weak plat_versal_net_gic_cpuif_enable
+#pragma weak plat_versal_net_gic_cpuif_disable
 #pragma weak plat_versal_net_gic_pcpu_init
+#pragma weak plat_versal_net_gic_redistif_on
+#pragma weak plat_versal_net_gic_redistif_off
 
 /* The GICv3 driver only needs to be initialized in EL3 */
 static uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT];
@@ -41,6 +44,13 @@
 };
 
 /*
+ * We save and restore the GICv3 context on system suspend. Allocate the
+ * data in the designated EL3 Secure carve-out memory.
+ */
+static gicv3_redist_ctx_t rdist_ctx __section("versal_net_el3_tzc_dram");
+static gicv3_dist_ctx_t dist_ctx __section("versal_net_el3_tzc_dram");
+
+/*
  * MPIDR hashing function for translating MPIDRs read from GICR_TYPER register
  * to core position.
  *
@@ -108,6 +118,14 @@
 }
 
 /******************************************************************************
+ * Versal NET common helper to disable the GIC CPU interface
+ *****************************************************************************/
+void plat_versal_net_gic_cpuif_disable(void)
+{
+	gicv3_cpuif_disable(plat_my_core_pos());
+}
+
+/******************************************************************************
  * Versal NET common helper to initialize the per-cpu redistributor interface in
  * GICv3
  *****************************************************************************/
@@ -134,3 +152,71 @@
 
 	gicv3_rdistif_init(plat_my_core_pos());
 }
+
+/******************************************************************************
+ * Versal NET common helpers to power GIC redistributor interface
+ *****************************************************************************/
+void plat_versal_net_gic_redistif_on(void)
+{
+	gicv3_rdistif_on(plat_my_core_pos());
+}
+
+void plat_versal_net_gic_redistif_off(void)
+{
+	gicv3_rdistif_off(plat_my_core_pos());
+}
+
+/******************************************************************************
+ * Versal NET common helper to save & restore the GICv3 on resume from system
+ * suspend
+ *****************************************************************************/
+void plat_versal_net_gic_save(void)
+{
+	/*
+	 * If an ITS is available, save its context before
+	 * the Redistributor using:
+	 * gicv3_its_save_disable(gits_base, &its_ctx[i])
+	 * Additionnaly, an implementation-defined sequence may
+	 * be required to save the whole ITS state.
+	 */
+
+	/*
+	 * Save the GIC Redistributors and ITS contexts before the
+	 * Distributor context. As we only handle SYSTEM SUSPEND API,
+	 * we only need to save the context of the CPU that is issuing
+	 * the SYSTEM SUSPEND call, i.e. the current CPU.
+	 */
+	gicv3_rdistif_save(plat_my_core_pos(), &rdist_ctx);
+
+	/* Save the GIC Distributor context */
+	gicv3_distif_save(&dist_ctx);
+
+	/*
+	 * From here, all the components of the GIC can be safely powered down
+	 * as long as there is an alternate way to handle wakeup interrupt
+	 * sources.
+	 */
+}
+
+void plat_versal_net_gic_resume(void)
+{
+	/* Restore the GIC Distributor context */
+	gicv3_distif_init_restore(&dist_ctx);
+
+	/*
+	 * Restore the GIC Redistributor and ITS contexts after the
+	 * Distributor context. As we only handle SYSTEM SUSPEND API,
+	 * we only need to restore the context of the CPU that issued
+	 * the SYSTEM SUSPEND call.
+	 */
+	gicv3_rdistif_init_restore(plat_my_core_pos(), &rdist_ctx);
+
+	/*
+	 * If an ITS is available, restore its context after
+	 * the Redistributor using:
+	 * gicv3_its_restore(gits_base, &its_ctx[i])
+	 * An implementation-defined sequence may be required to
+	 * restore the whole ITS state. The ITS must also be
+	 * re-enabled after this sequence has been executed.
+	 */
+}
diff --git a/plat/xilinx/versal_net/versal_net_ipi.c b/plat/xilinx/versal_net/versal_net_ipi.c
new file mode 100644
index 0000000..26ded89
--- /dev/null
+++ b/plat/xilinx/versal_net/versal_net_ipi.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022, Xilinx, Inc. All rights reserved.
+ * Copyright (C) 2022, Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * Versal NET IPI agent registers access management
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <common/debug.h>
+#include <common/runtime_svc.h>
+#include <lib/bakery_lock.h>
+#include <lib/mmio.h>
+
+#include <ipi.h>
+#include <plat_ipi.h>
+#include <plat_private.h>
+
+/* versal_net ipi configuration table */
+static const struct ipi_config versal_net_ipi_table[IPI_ID_MAX] = {
+	/* A72 IPI */
+	[IPI_ID_APU] = {
+		.ipi_bit_mask = IPI0_TRIG_BIT,
+		.ipi_reg_base = IPI0_REG_BASE,
+		.secure_only = 0,
+	},
+
+	/* PMC IPI */
+	[IPI_ID_PMC] = {
+		.ipi_bit_mask = PMC_IPI_TRIG_BIT,
+		.ipi_reg_base = IPI0_REG_BASE,
+		.secure_only = 0,
+	},
+
+	/* RPU0 IPI */
+	[IPI_ID_RPU0] = {
+		.ipi_bit_mask = IPI1_TRIG_BIT,
+		.ipi_reg_base = IPI1_REG_BASE,
+		.secure_only = 0,
+	},
+
+	/* RPU1 IPI */
+	[IPI_ID_RPU1] = {
+		.ipi_bit_mask = IPI2_TRIG_BIT,
+		.ipi_reg_base = IPI2_REG_BASE,
+		.secure_only = 0,
+	},
+
+	/* IPI3 IPI */
+	[IPI_ID_3] = {
+		.ipi_bit_mask = IPI3_TRIG_BIT,
+		.ipi_reg_base = IPI3_REG_BASE,
+		.secure_only = 0,
+	},
+
+	/* IPI4 IPI */
+	[IPI_ID_4] = {
+		.ipi_bit_mask = IPI4_TRIG_BIT,
+		.ipi_reg_base = IPI4_REG_BASE,
+		.secure_only = 0,
+	},
+
+	/* IPI5 IPI */
+	[IPI_ID_5] = {
+		.ipi_bit_mask = IPI5_TRIG_BIT,
+		.ipi_reg_base = IPI5_REG_BASE,
+		.secure_only = 0,
+	},
+};
+
+/* versal_net_ipi_config_table_init() - Initialize versal_net IPI configuration data
+ *
+ * @ipi_config_table  - IPI configuration table
+ * @ipi_total - Total number of IPI available
+ *
+ */
+void versal_net_ipi_config_table_init(void)
+{
+	ipi_config_table_init(versal_net_ipi_table, ARRAY_SIZE(versal_net_ipi_table));
+}