mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-27 07:15:20 +00:00
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
This commit is contained in:
parent
0bf622de68
commit
0654ab7f75
9 changed files with 820 additions and 3 deletions
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
264
plat/xilinx/versal_net/plat_psci_pm.c
Normal file
264
plat/xilinx/versal_net/plat_psci_pm.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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 \
|
||||
|
|
240
plat/xilinx/versal_net/pm_service/pm_client.c
Normal file
240
plat/xilinx/versal_net/pm_service/pm_client.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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];
|
||||
|
@ -40,6 +43,13 @@ static const interrupt_prop_t versal_net_interrupt_props[] = {
|
|||
PLAT_VERSAL_NET_G0_IRQ_PROPS(INTR_GROUP0)
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
@ -107,6 +117,14 @@ void plat_versal_net_gic_cpuif_enable(void)
|
|||
gicv3_cpuif_enable(plat_my_core_pos());
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
*/
|
||||
}
|
||||
|
|
85
plat/xilinx/versal_net/versal_net_ipi.c
Normal file
85
plat/xilinx/versal_net/versal_net_ipi.c
Normal file
|
@ -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));
|
||||
}
|
Loading…
Add table
Reference in a new issue