summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Buddhabhatti <jay.buddhabhatti@xilinx.com>2022-09-05 02:56:32 -0700
committerMichal Simek <michal.simek@amd.com>2022-09-20 19:02:42 +0200
commit0654ab7f75449307c79789e12be7aab2338edcc3 (patch)
tree0f3f48308280bfdb5581d778e0fe5f81e181c8d9
parent0bf622de68cd353a8406f76647b6afd8791d675d (diff)
downloadarm-trusted-firmware-0654ab7f75449307c79789e12be7aab2338edcc3.tar.gz
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
-rw-r--r--plat/xilinx/versal_net/aarch64/versal_net_common.c5
-rw-r--r--plat/xilinx/versal_net/bl31_versal_net_setup.c58
-rw-r--r--plat/xilinx/versal_net/include/plat_private.h16
-rw-r--r--plat/xilinx/versal_net/include/versal_net_def.h49
-rw-r--r--plat/xilinx/versal_net/plat_psci_pm.c264
-rw-r--r--plat/xilinx/versal_net/platform.mk20
-rw-r--r--plat/xilinx/versal_net/pm_service/pm_client.c240
-rw-r--r--plat/xilinx/versal_net/versal_net_gicv3.c86
-rw-r--r--plat/xilinx/versal_net/versal_net_ipi.c85
9 files changed, 820 insertions, 3 deletions
diff --git a/plat/xilinx/versal_net/aarch64/versal_net_common.c b/plat/xilinx/versal_net/aarch64/versal_net_common.c
index 46ebc3e49..91d0371d7 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 @@ void versal_net_config_setup(void)
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 ba23eb9d4..97080e91e 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 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
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_platform_setup(void)
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 6bd01ba54..6a3bc19d0 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 @@ const mmap_region_t *plat_versal_net_get_mmap(void);
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 @@ char *board_name_decode(void);
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 9b806832b..649886b63 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 000000000..8beaa9a57
--- /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 f361dfe59..08e65ac60 100644
--- a/plat/xilinx/versal_net/platform.mk
+++ b/plat/xilinx/versal_net/platform.mk
@@ -13,9 +13,14 @@ override RESET_TO_BL31 := 1
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 @@ PLAT_BL_COMMON_SOURCES := \
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 000000000..648732415
--- /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 028c1873c..b7ac6abbc 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 @@ static const interrupt_prop_t versal_net_interrupt_props[] = {
};
/*
+ * 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 @@ void plat_versal_net_gic_cpuif_enable(void)
}
/******************************************************************************
+ * 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 @@ void plat_versal_net_gic_pcpu_init(void)
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 000000000..26ded890e
--- /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));
+}