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:
Jay Buddhabhatti 2022-09-05 02:56:32 -07:00 committed by Michal Simek
parent 0bf622de68
commit 0654ab7f75
9 changed files with 820 additions and 3 deletions

View file

@ -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)

View file

@ -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();
}
}
/*

View file

@ -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)

View file

@ -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 */

View 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);
}

View file

@ -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 \

View 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);
}

View file

@ -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.
*/
}

View 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));
}