diff options
author | Jay Buddhabhatti <jay.buddhabhatti@xilinx.com> | 2022-09-05 02:56:32 -0700 |
---|---|---|
committer | Michal Simek <michal.simek@amd.com> | 2022-09-20 19:02:42 +0200 |
commit | 0654ab7f75449307c79789e12be7aab2338edcc3 (patch) | |
tree | 0f3f48308280bfdb5581d778e0fe5f81e181c8d9 | |
parent | 0bf622de68cd353a8406f76647b6afd8791d675d (diff) | |
download | arm-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.c | 5 | ||||
-rw-r--r-- | plat/xilinx/versal_net/bl31_versal_net_setup.c | 58 | ||||
-rw-r--r-- | plat/xilinx/versal_net/include/plat_private.h | 16 | ||||
-rw-r--r-- | plat/xilinx/versal_net/include/versal_net_def.h | 49 | ||||
-rw-r--r-- | plat/xilinx/versal_net/plat_psci_pm.c | 264 | ||||
-rw-r--r-- | plat/xilinx/versal_net/platform.mk | 20 | ||||
-rw-r--r-- | plat/xilinx/versal_net/pm_service/pm_client.c | 240 | ||||
-rw-r--r-- | plat/xilinx/versal_net/versal_net_gicv3.c | 86 | ||||
-rw-r--r-- | plat/xilinx/versal_net/versal_net_ipi.c | 85 |
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)); +} |