diff options
Diffstat (limited to 'plat/xilinx/common')
24 files changed, 3781 insertions, 0 deletions
diff --git a/plat/xilinx/common/include/ipi.h b/plat/xilinx/common/include/ipi.h new file mode 100644 index 0000000..1d62f3e --- /dev/null +++ b/plat/xilinx/common/include/ipi.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Xilinx IPI management configuration data and macros */ + +#ifndef IPI_H +#define IPI_H + +#include <stdint.h> + +/********************************************************************* + * IPI mailbox status macros + ********************************************************************/ +#define IPI_MB_STATUS_IDLE (0U) +#define IPI_MB_STATUS_SEND_PENDING (1U) +#define IPI_MB_STATUS_RECV_PENDING (2U) + +/********************************************************************* + * IPI mailbox call is secure or not macros + ********************************************************************/ +#define IPI_MB_CALL_NOTSECURE (0U) +#define IPI_MB_CALL_SECURE (1U) + +/********************************************************************* + * IPI secure check + ********************************************************************/ +#define IPI_SECURE_MASK (0x1U) +#define IPI_IS_SECURE(I) ((ipi_table[(I)].secure_only & \ + IPI_SECURE_MASK) ? 1 : 0) + +/********************************************************************* + * Struct definitions + ********************************************************************/ + +/* structure to maintain IPI configuration information */ +struct ipi_config { + unsigned int ipi_bit_mask; + unsigned int ipi_reg_base; + unsigned char secure_only; +}; + +/********************************************************************* + * IPI APIs declarations + ********************************************************************/ + +/* Initialize IPI configuration table */ +void ipi_config_table_init(const struct ipi_config *ipi_config_table, + uint32_t total_ipi); + +/* Validate IPI mailbox access */ +int ipi_mb_validate(uint32_t local, uint32_t remote, unsigned int is_secure); + +/* Open the IPI mailbox */ +void ipi_mb_open(uint32_t local, uint32_t remote); + +/* Release the IPI mailbox */ +void ipi_mb_release(uint32_t local, uint32_t remote); + +/* Enquire IPI mailbox status */ +int ipi_mb_enquire_status(uint32_t local, uint32_t remote); + +/* Trigger notification on the IPI mailbox */ +void ipi_mb_notify(uint32_t local, uint32_t remote, uint32_t is_blocking); + +/* Ack IPI mailbox notification */ +void ipi_mb_ack(uint32_t local, uint32_t remote); + +/* Disable IPI mailbox notification interrupt */ +void ipi_mb_disable_irq(uint32_t local, uint32_t remote); + +/* Enable IPI mailbox notification interrupt */ +void ipi_mb_enable_irq(uint32_t local, uint32_t remote); + +#endif /* IPI_H */ diff --git a/plat/xilinx/common/include/plat_common.h b/plat/xilinx/common/include/plat_common.h new file mode 100644 index 0000000..676baa2 --- /dev/null +++ b/plat/xilinx/common/include/plat_common.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Header file to contain common macros across different platforms */ +#ifndef PLAT_COMMON_H +#define PLAT_COMMON_H + +#define __bf_shf(x) (__builtin_ffsll(x) - 1U) +#define FIELD_GET(_mask, _reg) \ + ({ \ + (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ + }) + +#endif /* PLAT_COMMON_H */ diff --git a/plat/xilinx/common/include/plat_console.h b/plat/xilinx/common/include/plat_console.h new file mode 100644 index 0000000..0f8320e --- /dev/null +++ b/plat/xilinx/common/include/plat_console.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_DT_UART_H +#define PLAT_DT_UART_H + +#define DT_UART_DCC_COMPAT "arm,dcc" + +#if defined(PLAT_zynqmp) +#define DT_UART_COMPAT "xlnx,zynqmp-uart" +#else +#define DT_UART_COMPAT "arm,pl011" +#endif + +typedef struct dt_uart_info_s { + char compatible[30]; + uintptr_t base; + uint32_t baud_rate; + int32_t status; +} dt_uart_info_t; + +void setup_console(void); + +#endif /* PLAT_DT_UART_H */ diff --git a/plat/xilinx/common/include/plat_fdt.h b/plat/xilinx/common/include/plat_fdt.h new file mode 100644 index 0000000..a1ee1e1 --- /dev/null +++ b/plat/xilinx/common/include/plat_fdt.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef PLAT_FDT_H +#define PLAT_FDT_H + +void prepare_dtb(void); + +#endif /* PLAT_FDT_H */ diff --git a/plat/xilinx/common/include/plat_startup.h b/plat/xilinx/common/include/plat_startup.h new file mode 100644 index 0000000..5270e13 --- /dev/null +++ b/plat/xilinx/common/include/plat_startup.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_STARTUP_H +#define PLAT_STARTUP_H + +#include <common/bl_common.h> + +/* For Xilinx bootloader XBL handover */ +enum xbl_handoff { + XBL_HANDOFF_SUCCESS = 0, + XBL_HANDOFF_NO_STRUCT, + XBL_HANDOFF_INVAL_STRUCT, + XBL_HANDOFF_TOO_MANY_PARTS +}; + +#define XBL_MAX_PARTITIONS 8U + +/* Structure corresponding to each partition entry */ +struct xbl_partition { + uint64_t entry_point; + uint64_t flags; +}; + +/* Structure for handoff parameters to TrustedFirmware-A (TF-A) */ +struct xbl_handoff_params { + uint8_t magic[4]; + uint32_t num_entries; + struct xbl_partition partition[XBL_MAX_PARTITIONS]; +}; + +#define HANDOFF_PARAMS_MAX_SIZE sizeof(struct xbl_handoff_params) + +enum xbl_handoff xbl_handover(entry_point_info_t *bl32, + entry_point_info_t *bl33, + uint64_t handoff_addr); + +/* JEDEC Standard Manufacturer's Identification Code and Bank ID JEP106 */ +#define JEDEC_XILINX_MFID U(0x49) +#define JEDEC_XILINX_BKID U(0) + +#endif /* PLAT_STARTUP_H */ diff --git a/plat/xilinx/common/include/pm_api_sys.h b/plat/xilinx/common/include/pm_api_sys.h new file mode 100644 index 0000000..3fcb62f --- /dev/null +++ b/plat/xilinx/common/include/pm_api_sys.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PM_API_SYS_H +#define PM_API_SYS_H + +#include <stdint.h> +#include "pm_defs.h" + +/********************************************************************* + * Target module IDs macros + ********************************************************************/ +#define LIBPM_MODULE_ID 0x2U +#define LOADER_MODULE_ID 0x7U + +#define MODULE_ID_MASK 0x0000ff00U +/********************************************************** + * PM API function declarations + **********************************************************/ + +enum pm_ret_status pm_handle_eemi_call(uint32_t flag, uint32_t x0, uint32_t x1, + uint32_t x2, uint32_t x3, uint32_t x4, + uint32_t x5, uint64_t *result); +enum pm_ret_status pm_self_suspend(uint32_t nid, + uint32_t latency, + uint32_t state, + uintptr_t address, uint32_t flag); +enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason, uint32_t flag); +enum pm_ret_status pm_req_suspend(uint32_t target, + uint8_t ack, + uint32_t latency, + uint32_t state, uint32_t flag); +enum pm_ret_status pm_req_wakeup(uint32_t target, uint32_t set_address, + uintptr_t address, uint8_t ack, uint32_t flag); +enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t device_id, + uint8_t enable, uint32_t flag); +enum pm_ret_status pm_get_callbackdata(uint32_t *data, size_t count, uint32_t flag, + uint32_t ack); +void pm_client_set_wakeup_sources(uint32_t node_id); +enum pm_ret_status pm_pll_set_param(uint32_t clk_id, uint32_t param, + uint32_t value, uint32_t flag); +enum pm_ret_status pm_pll_get_param(uint32_t clk_id, uint32_t param, + uint32_t *value, uint32_t flag); +enum pm_ret_status pm_pll_set_mode(uint32_t clk_id, uint32_t mode, + uint32_t flag); +enum pm_ret_status pm_pll_get_mode(uint32_t clk_id, uint32_t *mode, + uint32_t flag); +enum pm_ret_status pm_force_powerdown(uint32_t target, uint8_t ack, + uint32_t flag); +enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype, + uint32_t flag); +enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id, + uint32_t arg1, uint32_t arg2, uint32_t arg3, + uint32_t *value, uint32_t flag); +enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2, + uint32_t arg3, uint32_t *data, uint32_t flag); +uint32_t pm_get_shutdown_scope(void); +enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *ret_payload, + uint32_t flag); +enum pm_ret_status pm_load_pdi(uint32_t src, uint32_t address_low, + uint32_t address_high, uint32_t flag); +enum pm_ret_status pm_register_notifier(uint32_t device_id, uint32_t event, + uint32_t wake, uint32_t enable, + uint32_t flag); +enum pm_ret_status pm_get_chipid(uint32_t *value); + +/* + * Assigning of argument values into array elements. + */ +#define PM_PACK_PAYLOAD1(pl, mid, flag, arg0) { \ + pl[0] = (uint32_t)(((uint32_t)(arg0) & 0xFFU) | ((mid) << 8U) | ((flag) << 24U)); \ +} + +#define PM_PACK_PAYLOAD2(pl, mid, flag, arg0, arg1) { \ + pl[1] = (uint32_t)(arg1); \ + PM_PACK_PAYLOAD1(pl, (mid), (flag), (arg0)); \ +} + +#define PM_PACK_PAYLOAD3(pl, mid, flag, arg0, arg1, arg2) { \ + pl[2] = (uint32_t)(arg2); \ + PM_PACK_PAYLOAD2(pl, (mid), (flag), (arg0), (arg1)); \ +} + +#define PM_PACK_PAYLOAD4(pl, mid, flag, arg0, arg1, arg2, arg3) { \ + pl[3] = (uint32_t)(arg3); \ + PM_PACK_PAYLOAD3(pl, (mid), (flag), (arg0), (arg1), (arg2)); \ +} + +#define PM_PACK_PAYLOAD5(pl, mid, flag, arg0, arg1, arg2, arg3, arg4) { \ + pl[4] = (uint32_t)(arg4); \ + PM_PACK_PAYLOAD4(pl, (mid), (flag), (arg0), (arg1), (arg2), (arg3)); \ +} + +#define PM_PACK_PAYLOAD6(pl, mid, flag, arg0, arg1, arg2, arg3, arg4, arg5) { \ + pl[5] = (uint32_t)(arg5); \ + PM_PACK_PAYLOAD5(pl, (mid), (flag), (arg0), (arg1), (arg2), (arg3), (arg4)); \ +} + +#endif /* PM_API_SYS_H */ diff --git a/plat/xilinx/common/include/pm_client.h b/plat/xilinx/common/include/pm_client.h new file mode 100644 index 0000000..a87923f --- /dev/null +++ b/plat/xilinx/common/include/pm_client.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Contains APU specific macros and macros to be defined depending on + * the execution environment. + */ + +#ifndef PM_CLIENT_H +#define PM_CLIENT_H + +#include "pm_common.h" +#include "pm_defs.h" + +/* Functions to be implemented by each PU */ +void pm_client_suspend(const struct pm_proc *proc, uint32_t state); +void pm_client_abort_suspend(void); +void pm_client_wakeup(const struct pm_proc *proc); + +#if !defined(PLAT_zynqmp) +enum pm_device_node_idx irq_to_pm_node_idx(uint32_t irq); +#endif + +/* Global variables to be set in pm_client.c */ +extern const struct pm_proc *primary_proc; + +#if defined(PLAT_zynqmp) +enum pm_ret_status pm_set_suspend_mode(uint32_t mode); +const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid); +#endif /* PLAT_zynqmp */ + +#endif /* PM_CLIENT_H */ diff --git a/plat/xilinx/common/include/pm_common.h b/plat/xilinx/common/include/pm_common.h new file mode 100644 index 0000000..c0308ab --- /dev/null +++ b/plat/xilinx/common/include/pm_common.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013-2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Contains definitions of commonly used macros and data types needed + * for PU Power Management. This file should be common for all PU's. + */ + +#ifndef PM_COMMON_H +#define PM_COMMON_H + +#include <stdint.h> +#include <plat_pm_common.h> + +#if IPI_CRC_CHECK +#define PAYLOAD_ARG_CNT 8U +#define IPI_W0_TO_W6_SIZE 28U +#define PAYLOAD_CRC_POS 7U +#define CRC_INIT_VALUE 0x4F4EU +#define CRC_ORDER 16U +#define CRC_POLYNOM 0x8005U +#else +#define PAYLOAD_ARG_CNT 6U +#endif +#define PAYLOAD_ARG_SIZE 4U /* size in bytes */ + +#define TZ_VERSION_MAJOR 1 +#define TZ_VERSION_MINOR 0 +#define TZ_VERSION ((TZ_VERSION_MAJOR << 16) | \ + TZ_VERSION_MINOR) + +/** + * struct pm_ipi - struct for capturing IPI-channel specific info. + * @local_ipi_id: Local IPI agent ID. + * @remote_ipi_id: Remote IPI Agent ID. + * @buffer_base: base address for payload buffer. + * + */ +struct pm_ipi { + const uint32_t local_ipi_id; + const uint32_t remote_ipi_id; + const uintptr_t buffer_base; +}; + +/** + * struct pm_proc - struct for capturing processor related info. + * @node_id: node-ID of the processor. + * @pwrdn_mask: cpu-specific mask to be used for power control register. + * @ipi: pointer to IPI channel structure. + * (in APU all processors share one IPI channel) + * + */ +struct pm_proc { + const uint32_t node_id; + const uint32_t pwrdn_mask; + const struct pm_ipi *ipi; +}; + +const struct pm_proc *pm_get_proc(uint32_t cpuid); + +#endif /* PM_COMMON_H */ diff --git a/plat/xilinx/common/include/pm_defs.h b/plat/xilinx/common/include/pm_defs.h new file mode 100644 index 0000000..9cdb0ba --- /dev/null +++ b/plat/xilinx/common/include/pm_defs.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Versal power management enums and defines */ + +#ifndef PM_DEFS_H +#define PM_DEFS_H + +#include "pm_node.h" + +/********************************************************************* + * Macro definitions + ********************************************************************/ + +/* State arguments of the self suspend */ +#define PM_STATE_CPU_IDLE 0x0U +#define PM_STATE_SUSPEND_TO_RAM 0xFU + +#define MAX_LATENCY (~0U) +#define MAX_QOS 100U + +/* Processor core device IDs */ +#define APU_DEVID(IDX) NODEID(XPM_NODECLASS_DEVICE, XPM_NODESUBCL_DEV_CORE, \ + XPM_NODETYPE_DEV_CORE_APU, (IDX)) + +#define XPM_DEVID_ACPU_0 APU_DEVID(XPM_NODEIDX_DEV_ACPU_0) +#define XPM_DEVID_ACPU_1 APU_DEVID(XPM_NODEIDX_DEV_ACPU_1) + +#define PERIPH_DEVID(IDX) NODEID((uint32_t)XPM_NODECLASS_DEVICE, \ + (uint32_t)XPM_NODESUBCL_DEV_PERIPH, \ + (uint32_t)XPM_NODETYPE_DEV_PERIPH, (IDX)) + +#define PM_GET_CALLBACK_DATA 0xa01U +#define PM_GET_TRUSTZONE_VERSION 0xa03U +#define TF_A_PM_REGISTER_SGI 0xa04U + +/* PM API Versions */ +#define PM_API_BASE_VERSION 1U +#define PM_API_VERSION_2 2U + +/* Loader API ids */ +#define PM_LOAD_PDI 0x701U +#define PM_LOAD_GET_HANDOFF_PARAMS 0x70BU + +/* System shutdown macros */ +#define XPM_SHUTDOWN_TYPE_SHUTDOWN 0U +#define XPM_SHUTDOWN_TYPE_RESET 1U +#define XPM_SHUTDOWN_TYPE_SETSCOPE_ONLY 2U + +#define XPM_SHUTDOWN_SUBTYPE_RST_SUBSYSTEM 0U +#define XPM_SHUTDOWN_SUBTYPE_RST_PS_ONLY 1U +#define XPM_SHUTDOWN_SUBTYPE_RST_SYSTEM 2U + +/********************************************************************* + * Enum definitions + ********************************************************************/ + +/* + * ioctl id + */ +enum { + IOCTL_GET_RPU_OPER_MODE = 0, + IOCTL_SET_RPU_OPER_MODE = 1, + IOCTL_RPU_BOOT_ADDR_CONFIG = 2, + IOCTL_TCM_COMB_CONFIG = 3, + IOCTL_SET_TAPDELAY_BYPASS = 4, + IOCTL_SD_DLL_RESET = 6, + IOCTL_SET_SD_TAPDELAY = 7, + /* Ioctl for clock driver */ + IOCTL_SET_PLL_FRAC_MODE = 8, + IOCTL_GET_PLL_FRAC_MODE = 9, + IOCTL_SET_PLL_FRAC_DATA = 10, + IOCTL_GET_PLL_FRAC_DATA = 11, + IOCTL_WRITE_GGS = 12, + IOCTL_READ_GGS = 13, + IOCTL_WRITE_PGGS = 14, + IOCTL_READ_PGGS = 15, + /* IOCTL for ULPI reset */ + IOCTL_ULPI_RESET = 16, + /* Set healthy bit value */ + IOCTL_SET_BOOT_HEALTH_STATUS = 17, + IOCTL_AFI = 18, + /* Probe counter read/write */ + IOCTL_PROBE_COUNTER_READ = 19, + IOCTL_PROBE_COUNTER_WRITE = 20, + IOCTL_OSPI_MUX_SELECT = 21, + /* IOCTL for USB power request */ + IOCTL_USB_SET_STATE = 22, + /* IOCTL to get last reset reason */ + IOCTL_GET_LAST_RESET_REASON = 23, + /* AI engine NPI ISR clear */ + IOCTL_AIE_ISR_CLEAR = 24, + /* Register SGI to TF-A */ + IOCTL_SET_SGI = 25, +}; + +/** + * enum pm_pll_param - enum represents the parameters for a phase-locked loop. + * @PM_PLL_PARAM_DIV2: Enable for divide by 2 function inside the PLL. + * @PM_PLL_PARAM_FBDIV: Feedback divisor integer portion for the PLL. + * @PM_PLL_PARAM_DATA: Feedback divisor fractional portion for the PLL. + * @PM_PLL_PARAM_PRE_SRC: Clock source for PLL input. + * @PM_PLL_PARAM_POST_SRC: Clock source for PLL Bypass mode. + * @PM_PLL_PARAM_LOCK_DLY: Lock circuit config settings for lock windowsize. + * @PM_PLL_PARAM_LOCK_CNT: Lock circuit counter setting. + * @PM_PLL_PARAM_LFHF: PLL loop filter high frequency capacitor control. + * @PM_PLL_PARAM_CP: PLL charge pump control. + * @PM_PLL_PARAM_RES: PLL loop filter resistor control. + * @PM_PLL_PARAM_MAX: Represents the maximum parameter value for the PLL + */ +enum pm_pll_param { + PM_PLL_PARAM_DIV2, + PM_PLL_PARAM_FBDIV, + PM_PLL_PARAM_DATA, + PM_PLL_PARAM_PRE_SRC, + PM_PLL_PARAM_POST_SRC, + PM_PLL_PARAM_LOCK_DLY, + PM_PLL_PARAM_LOCK_CNT, + PM_PLL_PARAM_LFHF, + PM_PLL_PARAM_CP, + PM_PLL_PARAM_RES, + PM_PLL_PARAM_MAX, +}; + +enum pm_api_id { + /* Miscellaneous API functions: */ + PM_GET_API_VERSION = 1, /* Do not change or move */ + PM_SET_CONFIGURATION, + PM_GET_NODE_STATUS, + PM_GET_OP_CHARACTERISTIC, + PM_REGISTER_NOTIFIER, + /* API for suspending of PUs: */ + PM_REQ_SUSPEND, + PM_SELF_SUSPEND, + PM_FORCE_POWERDOWN, + PM_ABORT_SUSPEND, + PM_REQ_WAKEUP, + PM_SET_WAKEUP_SOURCE, + PM_SYSTEM_SHUTDOWN, + /* API for managing PM slaves: */ + PM_REQ_NODE, + PM_RELEASE_NODE, + PM_SET_REQUIREMENT, + PM_SET_MAX_LATENCY, + /* Direct control API functions: */ + PM_RESET_ASSERT, + PM_RESET_GET_STATUS, + PM_MMIO_WRITE, + PM_MMIO_READ, + PM_INIT_FINALIZE, + PM_FPGA_LOAD, + PM_FPGA_GET_STATUS, + PM_GET_CHIPID, + PM_SECURE_RSA_AES, + PM_SECURE_SHA, + PM_SECURE_RSA, + PM_PINCTRL_REQUEST, + PM_PINCTRL_RELEASE, + PM_PINCTRL_GET_FUNCTION, + PM_PINCTRL_SET_FUNCTION, + PM_PINCTRL_CONFIG_PARAM_GET, + PM_PINCTRL_CONFIG_PARAM_SET, + PM_IOCTL, + /* API to query information from firmware */ + PM_QUERY_DATA, + /* Clock control API functions */ + PM_CLOCK_ENABLE, + PM_CLOCK_DISABLE, + PM_CLOCK_GETSTATE, + PM_CLOCK_SETDIVIDER, + PM_CLOCK_GETDIVIDER, + PM_CLOCK_SETPARENT = 43, + PM_CLOCK_GETPARENT, + PM_SECURE_IMAGE, + /* FPGA PL Readback */ + PM_FPGA_READ, + PM_SECURE_AES, + /* PLL control API functions */ + PM_PLL_SET_PARAMETER, + PM_PLL_GET_PARAMETER, + PM_PLL_SET_MODE, + PM_PLL_GET_MODE, + /* PM Register Access API */ + PM_REGISTER_ACCESS, + PM_EFUSE_ACCESS, + PM_FPGA_GET_VERSION, + PM_FPGA_GET_FEATURE_LIST, + PM_FEATURE_CHECK = 63, + PM_API_MAX = 74 +}; + +enum pm_abort_reason { + ABORT_REASON_WKUP_EVENT = 100, + ABORT_REASON_PU_BUSY, + ABORT_REASON_NO_PWRDN, + ABORT_REASON_UNKNOWN, +}; + +enum pm_opchar_type { + PM_OPCHAR_TYPE_POWER = 1, + PM_OPCHAR_TYPE_TEMP, + PM_OPCHAR_TYPE_LATENCY, +}; + +/* + * Subsystem IDs + */ +typedef enum { + XPM_SUBSYSID_PMC, + XPM_SUBSYSID_PSM, + XPM_SUBSYSID_APU, + XPM_SUBSYSID_RPU0_LOCK, + XPM_SUBSYSID_RPU0_0, + XPM_SUBSYSID_RPU0_1, + XPM_SUBSYSID_DDR0, + XPM_SUBSYSID_ME, + XPM_SUBSYSID_PL, + XPM_SUBSYSID_MAX, +} XPm_SubsystemId; + +/* TODO: move pm_ret_status from device specific location to common location */ +/** + * enum pm_ret_status - enum represents the return status codes for a PM + * operation. + * @PM_RET_SUCCESS: success. + * @PM_RET_ERROR_ARGS: illegal arguments provided (deprecated). + * @PM_RET_ERROR_NOTSUPPORTED: feature not supported (deprecated). + * @PM_RET_ERROR_NOFEATURE: feature is not available. + * @PM_RET_ERROR_INVALID_CRC: invalid crc in IPI communication. + * @PM_RET_ERROR_NOT_ENABLED: feature is not enabled. + * @PM_RET_ERROR_INTERNAL: internal error. + * @PM_RET_ERROR_CONFLICT: conflict. + * @PM_RET_ERROR_ACCESS: access rights violation. + * @PM_RET_ERROR_INVALID_NODE: invalid node. + * @PM_RET_ERROR_DOUBLE_REQ: duplicate request for same node. + * @PM_RET_ERROR_ABORT_SUSPEND: suspend procedure has been aborted. + * @PM_RET_ERROR_TIMEOUT: timeout in communication with PMU. + * @PM_RET_ERROR_NODE_USED: node is already in use. + * @PM_RET_ERROR_NO_FEATURE: indicates that the requested feature is not + * supported. + */ +enum pm_ret_status { + PM_RET_SUCCESS, + PM_RET_ERROR_ARGS = 1, + PM_RET_ERROR_NOTSUPPORTED = 4, + PM_RET_ERROR_NOFEATURE = 19, + PM_RET_ERROR_INVALID_CRC = 301, + PM_RET_ERROR_NOT_ENABLED = 29, + PM_RET_ERROR_INTERNAL = 2000, + PM_RET_ERROR_CONFLICT = 2001, + PM_RET_ERROR_ACCESS = 2002, + PM_RET_ERROR_INVALID_NODE = 2003, + PM_RET_ERROR_DOUBLE_REQ = 2004, + PM_RET_ERROR_ABORT_SUSPEND = 2005, + PM_RET_ERROR_TIMEOUT = 2006, + PM_RET_ERROR_NODE_USED = 2007, + PM_RET_ERROR_NO_FEATURE = 2008 +}; + +/* + * Qids + */ +enum pm_query_id { + XPM_QID_INVALID, + XPM_QID_CLOCK_GET_NAME, + XPM_QID_CLOCK_GET_TOPOLOGY, + XPM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS, + XPM_QID_CLOCK_GET_MUXSOURCES, + XPM_QID_CLOCK_GET_ATTRIBUTES, + XPM_QID_PINCTRL_GET_NUM_PINS, + XPM_QID_PINCTRL_GET_NUM_FUNCTIONS, + XPM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS, + XPM_QID_PINCTRL_GET_FUNCTION_NAME, + XPM_QID_PINCTRL_GET_FUNCTION_GROUPS, + XPM_QID_PINCTRL_GET_PIN_GROUPS, + XPM_QID_CLOCK_GET_NUM_CLOCKS, + XPM_QID_CLOCK_GET_MAX_DIVISOR, + XPM_QID_PLD_GET_PARENT, +}; +#endif /* PM_DEFS_H */ diff --git a/plat/xilinx/common/include/pm_ipi.h b/plat/xilinx/common/include/pm_ipi.h new file mode 100644 index 0000000..976abd6 --- /dev/null +++ b/plat/xilinx/common/include/pm_ipi.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PM_IPI_H +#define PM_IPI_H + +#include <stddef.h> + +#include <plat_ipi.h> +#include "pm_common.h" + +#define IPI_BLOCKING 1 +#define IPI_NON_BLOCKING 0 + +void pm_ipi_init(const struct pm_proc *proc); + +enum pm_ret_status pm_ipi_send(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT]); +enum pm_ret_status pm_ipi_send_non_blocking(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT]); +enum pm_ret_status pm_ipi_send_sync(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT], + uint32_t *value, size_t count); +enum pm_ret_status pm_ipi_buff_read_callb(uint32_t *value, size_t count); +void pm_ipi_irq_enable(const struct pm_proc *proc); +void pm_ipi_irq_clear(const struct pm_proc *proc); +uint32_t pm_ipi_irq_status(const struct pm_proc *proc); +#if IPI_CRC_CHECK +uint32_t calculate_crc(uint32_t payload[PAYLOAD_ARG_CNT], uint32_t buffersize); +#endif + +#endif /* PM_IPI_H */ diff --git a/plat/xilinx/common/include/pm_node.h b/plat/xilinx/common/include/pm_node.h new file mode 100644 index 0000000..46f6bcf --- /dev/null +++ b/plat/xilinx/common/include/pm_node.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Versal PM nodes enums and defines */ + +#ifndef PM_NODE_H +#define PM_NODE_H + +/********************************************************************* + * Macro definitions + ********************************************************************/ + +#define NODE_CLASS_SHIFT 26U +#define NODE_SUBCLASS_SHIFT 20U +#define NODE_TYPE_SHIFT 14U +#define NODE_INDEX_SHIFT 0U +#define NODE_CLASS_MASK_BITS GENMASK_32(5, 0) +#define NODE_SUBCLASS_MASK_BITS GENMASK_32(5, 0) +#define NODE_TYPE_MASK_BITS GENMASK_32(5, 0) +#define NODE_INDEX_MASK_BITS GENMASK_32(13, 0) +#define NODE_CLASS_MASK (NODE_CLASS_MASK_BITS << NODE_CLASS_SHIFT) +#define NODE_SUBCLASS_MASK (NODE_SUBCLASS_MASK_BITS << NODE_SUBCLASS_SHIFT) +#define NODE_TYPE_MASK (NODE_TYPE_MASK_BITS << NODE_TYPE_SHIFT) +#define NODE_INDEX_MASK (NODE_INDEX_MASK_BITS << NODE_INDEX_SHIFT) + +#define NODEID(CLASS, SUBCLASS, TYPE, INDEX) \ + ((((CLASS) & NODE_CLASS_MASK_BITS) << NODE_CLASS_SHIFT) | \ + (((SUBCLASS) & NODE_SUBCLASS_MASK_BITS) << NODE_SUBCLASS_SHIFT) | \ + (((TYPE) & NODE_TYPE_MASK_BITS) << NODE_TYPE_SHIFT) | \ + (((INDEX) & NODE_INDEX_MASK_BITS) << NODE_INDEX_SHIFT)) + +#define NODECLASS(ID) (((ID) & NODE_CLASS_MASK) >> NODE_CLASS_SHIFT) +#define NODESUBCLASS(ID) (((ID) & NODE_SUBCLASS_MASK) >> \ + NODE_SUBCLASS_SHIFT) +#define NODETYPE(ID) (((ID) & NODE_TYPE_MASK) >> NODE_TYPE_SHIFT) +#define NODEINDEX(ID) (((ID) & NODE_INDEX_MASK) >> NODE_INDEX_SHIFT) + +/********************************************************************* + * Enum definitions + ********************************************************************/ + +/* Node class types */ +enum pm_node_class { + XPM_NODECLASS_MIN, + + XPM_NODECLASS_POWER, + XPM_NODECLASS_CLOCK, + XPM_NODECLASS_RESET, + XPM_NODECLASS_MEMIC, + XPM_NODECLASS_STMIC, + XPM_NODECLASS_DEVICE, + + XPM_NODECLASS_MAX +}; + +enum pm_device_node_subclass { + /* Device types */ + XPM_NODESUBCL_DEV_CORE = 1, + XPM_NODESUBCL_DEV_PERIPH, + XPM_NODESUBCL_DEV_MEM, + XPM_NODESUBCL_DEV_SOC, + XPM_NODESUBCL_DEV_MEM_CTRLR, + XPM_NODESUBCL_DEV_PHY, +}; + +enum pm_device_node_type { + /* Device types */ + XPM_NODETYPE_DEV_CORE_PMC = 1, + XPM_NODETYPE_DEV_CORE_PSM, + XPM_NODETYPE_DEV_CORE_APU, + XPM_NODETYPE_DEV_CORE_RPU, + XPM_NODETYPE_DEV_OCM, + XPM_NODETYPE_DEV_TCM, + XPM_NODETYPE_DEV_L2CACHE, + XPM_NODETYPE_DEV_DDR, + XPM_NODETYPE_DEV_PERIPH, + XPM_NODETYPE_DEV_SOC, + XPM_NODETYPE_DEV_GT, +}; + +/* Device node Indexes */ +enum pm_device_node_idx { + /* Device nodes */ + XPM_NODEIDX_DEV_MIN = 0x0, + + /* Processor devices */ + XPM_NODEIDX_DEV_PMC_PROC = 0x1, + XPM_NODEIDX_DEV_PSM_PROC = 0x2, + XPM_NODEIDX_DEV_ACPU_0 = 0x3, + XPM_NODEIDX_DEV_ACPU_1 = 0x4, + XPM_NODEIDX_DEV_RPU0_0 = 0x5, + XPM_NODEIDX_DEV_RPU0_1 = 0x6, + + /* Memory devices */ + XPM_NODEIDX_DEV_OCM_0 = 0x7, + XPM_NODEIDX_DEV_OCM_1 = 0x8, + XPM_NODEIDX_DEV_OCM_2 = 0x9, + XPM_NODEIDX_DEV_OCM_3 = 0xA, + XPM_NODEIDX_DEV_TCM_0_A = 0xB, + XPM_NODEIDX_DEV_TCM_0_B = 0xC, + XPM_NODEIDX_DEV_TCM_1_A = 0xD, + XPM_NODEIDX_DEV_TCM_1_B = 0xE, + XPM_NODEIDX_DEV_L2_BANK_0 = 0xF, + XPM_NODEIDX_DEV_DDR_0 = 0x10, + XPM_NODEIDX_DEV_DDR_1 = 0x11, + XPM_NODEIDX_DEV_DDR_2 = 0x12, + XPM_NODEIDX_DEV_DDR_3 = 0x13, + XPM_NODEIDX_DEV_DDR_4 = 0x14, + XPM_NODEIDX_DEV_DDR_5 = 0x15, + XPM_NODEIDX_DEV_DDR_6 = 0x16, + XPM_NODEIDX_DEV_DDR_7 = 0x17, + + /* LPD Peripheral devices */ + XPM_NODEIDX_DEV_USB_0 = 0x18, + XPM_NODEIDX_DEV_GEM_0 = 0x19, + XPM_NODEIDX_DEV_GEM_1 = 0x1A, + XPM_NODEIDX_DEV_SPI_0 = 0x1B, + XPM_NODEIDX_DEV_SPI_1 = 0x1C, + XPM_NODEIDX_DEV_I2C_0 = 0x1D, + XPM_NODEIDX_DEV_I2C_1 = 0x1E, + XPM_NODEIDX_DEV_CAN_FD_0 = 0x1F, + XPM_NODEIDX_DEV_CAN_FD_1 = 0x20, + XPM_NODEIDX_DEV_UART_0 = 0x21, + XPM_NODEIDX_DEV_UART_1 = 0x22, + XPM_NODEIDX_DEV_GPIO = 0x23, + XPM_NODEIDX_DEV_TTC_0 = 0x24, + XPM_NODEIDX_DEV_TTC_1 = 0x25, + XPM_NODEIDX_DEV_TTC_2 = 0x26, + XPM_NODEIDX_DEV_TTC_3 = 0x27, + XPM_NODEIDX_DEV_SWDT_LPD = 0x28, + + /* FPD Peripheral devices */ + XPM_NODEIDX_DEV_SWDT_FPD = 0x29, + + /* PMC Peripheral devices */ + XPM_NODEIDX_DEV_OSPI = 0x2A, + XPM_NODEIDX_DEV_QSPI = 0x2B, + XPM_NODEIDX_DEV_GPIO_PMC = 0x2C, + XPM_NODEIDX_DEV_I2C_PMC = 0x2D, + XPM_NODEIDX_DEV_SDIO_0 = 0x2E, + XPM_NODEIDX_DEV_SDIO_1 = 0x2F, + + XPM_NODEIDX_DEV_PL_0 = 0x30, + XPM_NODEIDX_DEV_PL_1 = 0x31, + XPM_NODEIDX_DEV_PL_2 = 0x32, + XPM_NODEIDX_DEV_PL_3 = 0x33, + XPM_NODEIDX_DEV_RTC = 0x34, + XPM_NODEIDX_DEV_ADMA_0 = 0x35, + XPM_NODEIDX_DEV_ADMA_1 = 0x36, + XPM_NODEIDX_DEV_ADMA_2 = 0x37, + XPM_NODEIDX_DEV_ADMA_3 = 0x38, + XPM_NODEIDX_DEV_ADMA_4 = 0x39, + XPM_NODEIDX_DEV_ADMA_5 = 0x3A, + XPM_NODEIDX_DEV_ADMA_6 = 0x3B, + XPM_NODEIDX_DEV_ADMA_7 = 0x3C, + XPM_NODEIDX_DEV_IPI_0 = 0x3D, + XPM_NODEIDX_DEV_IPI_1 = 0x3E, + XPM_NODEIDX_DEV_IPI_2 = 0x3F, + XPM_NODEIDX_DEV_IPI_3 = 0x40, + XPM_NODEIDX_DEV_IPI_4 = 0x41, + XPM_NODEIDX_DEV_IPI_5 = 0x42, + XPM_NODEIDX_DEV_IPI_6 = 0x43, + + /* Entire SoC */ + XPM_NODEIDX_DEV_SOC = 0x44, + + /* DDR memory controllers */ + XPM_NODEIDX_DEV_DDRMC_0 = 0x45, + XPM_NODEIDX_DEV_DDRMC_1 = 0x46, + XPM_NODEIDX_DEV_DDRMC_2 = 0x47, + XPM_NODEIDX_DEV_DDRMC_3 = 0x48, + + /* GT devices */ + XPM_NODEIDX_DEV_GT_0 = 0x49, + XPM_NODEIDX_DEV_GT_1 = 0x4A, + XPM_NODEIDX_DEV_GT_2 = 0x4B, + XPM_NODEIDX_DEV_GT_3 = 0x4C, + XPM_NODEIDX_DEV_GT_4 = 0x4D, + XPM_NODEIDX_DEV_GT_5 = 0x4E, + XPM_NODEIDX_DEV_GT_6 = 0x4F, + XPM_NODEIDX_DEV_GT_7 = 0x50, + XPM_NODEIDX_DEV_GT_8 = 0x51, + XPM_NODEIDX_DEV_GT_9 = 0x52, + XPM_NODEIDX_DEV_GT_10 = 0x53, + +#if defined(PLAT_versal_net) + XPM_NODEIDX_DEV_ACPU_0_0 = 0xAF, + XPM_NODEIDX_DEV_ACPU_0_1 = 0xB0, + XPM_NODEIDX_DEV_ACPU_0_2 = 0xB1, + XPM_NODEIDX_DEV_ACPU_0_3 = 0xB2, + XPM_NODEIDX_DEV_ACPU_1_0 = 0xB3, + XPM_NODEIDX_DEV_ACPU_1_1 = 0xB4, + XPM_NODEIDX_DEV_ACPU_1_2 = 0xB5, + XPM_NODEIDX_DEV_ACPU_1_3 = 0xB6, + XPM_NODEIDX_DEV_ACPU_2_0 = 0xB7, + XPM_NODEIDX_DEV_ACPU_2_1 = 0xB8, + XPM_NODEIDX_DEV_ACPU_2_2 = 0xB9, + XPM_NODEIDX_DEV_ACPU_2_3 = 0xBA, + XPM_NODEIDX_DEV_ACPU_3_0 = 0xBB, + XPM_NODEIDX_DEV_ACPU_3_1 = 0xBC, + XPM_NODEIDX_DEV_ACPU_3_2 = 0xBD, + XPM_NODEIDX_DEV_ACPU_3_3 = 0xBE, + XPM_NODEIDX_DEV_RPU_A_0 = 0xBF, + XPM_NODEIDX_DEV_RPU_A_1 = 0xC0, + XPM_NODEIDX_DEV_RPU_B_0 = 0xC1, + XPM_NODEIDX_DEV_RPU_B_1 = 0xC2, + XPM_NODEIDX_DEV_OCM_0_0 = 0xC3, + XPM_NODEIDX_DEV_OCM_0_1 = 0xC4, + XPM_NODEIDX_DEV_OCM_0_2 = 0xC5, + XPM_NODEIDX_DEV_OCM_0_3 = 0xC6, + XPM_NODEIDX_DEV_OCM_1_0 = 0xC7, + XPM_NODEIDX_DEV_OCM_1_1 = 0xC8, + XPM_NODEIDX_DEV_OCM_1_2 = 0xC9, + XPM_NODEIDX_DEV_OCM_1_3 = 0xCA, + XPM_NODEIDX_DEV_TCM_A_0A = 0xCB, + XPM_NODEIDX_DEV_TCM_A_0B = 0xCC, + XPM_NODEIDX_DEV_TCM_A_0C = 0xCD, + XPM_NODEIDX_DEV_TCM_A_1A = 0xCE, + XPM_NODEIDX_DEV_TCM_A_1B = 0xCF, + XPM_NODEIDX_DEV_TCM_A_1C = 0xD0, + XPM_NODEIDX_DEV_TCM_B_0A = 0xD1, + XPM_NODEIDX_DEV_TCM_B_0B = 0xD2, + XPM_NODEIDX_DEV_TCM_B_0C = 0xD3, + XPM_NODEIDX_DEV_TCM_B_1A = 0xD4, + XPM_NODEIDX_DEV_TCM_B_1B = 0xD5, + XPM_NODEIDX_DEV_TCM_B_1C = 0xD6, + XPM_NODEIDX_DEV_USB_1 = 0xD7, + XPM_NODEIDX_DEV_PMC_WWDT = 0xD8, + XPM_NODEIDX_DEV_LPD_SWDT_0 = 0xD9, + XPM_NODEIDX_DEV_LPD_SWDT_1 = 0xDA, + XPM_NODEIDX_DEV_FPD_SWDT_0 = 0xDB, + XPM_NODEIDX_DEV_FPD_SWDT_1 = 0xDC, + XPM_NODEIDX_DEV_FPD_SWDT_2 = 0xDD, + XPM_NODEIDX_DEV_FPD_SWDT_3 = 0xDE, +#endif + XPM_NODEIDX_DEV_MAX, +}; + +#endif /* PM_NODE_H */ diff --git a/plat/xilinx/common/include/pm_svc_main.h b/plat/xilinx/common/include/pm_svc_main.h new file mode 100644 index 0000000..4cf7727 --- /dev/null +++ b/plat/xilinx/common/include/pm_svc_main.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PM_SVC_MAIN_H +#define PM_SVC_MAIN_H + +#include <pm_common.h> + +/******************************************************************************/ +/** + * SECURE_REDUNDANT_CALL() - Adds redundancy to the function call. This is to + * avoid glitches which can skip a function call + * and cause altering of the code flow in security + * critical functions. + * @status: Variable which holds the return value of function executed + * @status_tmp: Variable which holds the return value of redundant function + * call executed + * @function: Function to be executed + * + * Return: None + * + ******************************************************************************/ +#define SECURE_REDUNDANT_CALL(status, status_tmp, function, ...) \ + { \ + status = function(__VA_ARGS__); \ + status_tmp = function(__VA_ARGS__); \ + } + +int32_t pm_setup(void); +uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, const void *cookie, void *handle, + uint64_t flags); + +int32_t pm_register_sgi(uint32_t sgi_num, uint32_t reset); +#endif /* PM_SVC_MAIN_H */ diff --git a/plat/xilinx/common/ipi.c b/plat/xilinx/common/ipi.c new file mode 100644 index 0000000..399d283 --- /dev/null +++ b/plat/xilinx/common/ipi.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Xilinx 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_private.h> + +/********************************************************************* + * Macros definitions + ********************************************************************/ + +/* IPI registers offsets macros */ +#define IPI_TRIG_OFFSET 0x00U +#define IPI_OBR_OFFSET 0x04U +#define IPI_ISR_OFFSET 0x10U +#define IPI_IMR_OFFSET 0x14U +#define IPI_IER_OFFSET 0x18U +#define IPI_IDR_OFFSET 0x1CU + +/* IPI register start offset */ +#define IPI_REG_BASE(I) (ipi_table[(I)].ipi_reg_base) + +/* IPI register bit mask */ +#define IPI_BIT_MASK(I) (ipi_table[(I)].ipi_bit_mask) + +/* IPI configuration table */ +static const struct ipi_config *ipi_table; + +/* Total number of IPI */ +static uint32_t ipi_total; + +/** + * ipi_config_table_init() - Initialize IPI configuration data. + * @ipi_config_table: IPI configuration table. + * @total_ipi: Total number of IPI available. + * + */ +void ipi_config_table_init(const struct ipi_config *ipi_config_table, + uint32_t total_ipi) +{ + ipi_table = ipi_config_table; + ipi_total = total_ipi; +} + +/** + * is_ipi_mb_within_range() - verify if IPI mailbox is within range. + * @local: local IPI ID. + * @remote: remote IPI ID. + * + * Return: - 1 if within range, 0 if not. + * + */ +static inline int is_ipi_mb_within_range(uint32_t local, uint32_t remote) +{ + int ret = 1; + + if (remote >= ipi_total || local >= ipi_total) { + ret = 0; + } + + return ret; +} + +/** + * ipi_mb_validate() - validate IPI mailbox access. + * @local: local IPI ID. + * @remote: remote IPI ID. + * @is_secure: indicate if the requester is from secure software. + * + * Return: 0 success, negative value for errors. + * + */ +int ipi_mb_validate(uint32_t local, uint32_t remote, unsigned int is_secure) +{ + int ret = 0; + + if (!is_ipi_mb_within_range(local, remote)) { + ret = -EINVAL; + } else if (IPI_IS_SECURE(local) && !is_secure) { + ret = -EPERM; + } else if (IPI_IS_SECURE(remote) && !is_secure) { + ret = -EPERM; + } else { + /* To fix the misra 15.7 warning */ + } + + return ret; +} + +/** + * ipi_mb_open() - Open IPI mailbox. + * @local: local IPI ID. + * @remote: remote IPI ID. + * + */ +void ipi_mb_open(uint32_t local, uint32_t remote) +{ + mmio_write_32(IPI_REG_BASE(local) + IPI_IDR_OFFSET, + IPI_BIT_MASK(remote)); + mmio_write_32(IPI_REG_BASE(local) + IPI_ISR_OFFSET, + IPI_BIT_MASK(remote)); +} + +/** + * ipi_mb_release() - Open IPI mailbox. + * @local: local IPI ID. + * @remote: remote IPI ID. + * + */ +void ipi_mb_release(uint32_t local, uint32_t remote) +{ + mmio_write_32(IPI_REG_BASE(local) + IPI_IDR_OFFSET, + IPI_BIT_MASK(remote)); +} + +/** + * ipi_mb_enquire_status() - Enquire IPI mailbox status. + * @local: local IPI ID. + * @remote: remote IPI ID. + * + * Return: 0 idle, positive value for pending sending or receiving, + * negative value for errors. + * + */ +int ipi_mb_enquire_status(uint32_t local, uint32_t remote) +{ + int ret = 0U; + uint32_t status; + + status = mmio_read_32(IPI_REG_BASE(local) + IPI_OBR_OFFSET); + if (status & IPI_BIT_MASK(remote)) { + ret |= IPI_MB_STATUS_SEND_PENDING; + } + status = mmio_read_32(IPI_REG_BASE(local) + IPI_ISR_OFFSET); + if (status & IPI_BIT_MASK(remote)) { + ret |= IPI_MB_STATUS_RECV_PENDING; + } + + return ret; +} + +/** + * ipi_mb_notify() - Trigger IPI mailbox notification. + * @local: local IPI ID. + * @remote: remote IPI ID. + * @is_blocking: if to trigger the notification in blocking mode or not. + * + * It sets the remote bit in the IPI agent trigger register. + * + */ +void ipi_mb_notify(uint32_t local, uint32_t remote, uint32_t is_blocking) +{ + uint32_t status; + + mmio_write_32(IPI_REG_BASE(local) + IPI_TRIG_OFFSET, + IPI_BIT_MASK(remote)); + if (is_blocking) { + do { + status = mmio_read_32(IPI_REG_BASE(local) + + IPI_OBR_OFFSET); + } while (status & IPI_BIT_MASK(remote)); + } +} + +/** + * ipi_mb_ack() - Ack IPI mailbox notification from the other end. + * @local: local IPI ID. + * @remote: remote IPI ID. + * + * It will clear the remote bit in the isr register. + * + */ +void ipi_mb_ack(uint32_t local, uint32_t remote) +{ + mmio_write_32(IPI_REG_BASE(local) + IPI_ISR_OFFSET, + IPI_BIT_MASK(remote)); +} + +/** + * ipi_mb_disable_irq() - Disable IPI mailbox notification interrupt. + * @local: local IPI ID. + * @remote: remote IPI ID. + * + * It will mask the remote bit in the idr register. + * + */ +void ipi_mb_disable_irq(uint32_t local, uint32_t remote) +{ + mmio_write_32(IPI_REG_BASE(local) + IPI_IDR_OFFSET, + IPI_BIT_MASK(remote)); +} + +/** + * ipi_mb_enable_irq() - Enable IPI mailbox notification interrupt. + * @local: local IPI ID. + * @remote: remote IPI ID. + * + * It will mask the remote bit in the idr register. + * + */ +void ipi_mb_enable_irq(uint32_t local, uint32_t remote) +{ + mmio_write_32(IPI_REG_BASE(local) + IPI_IER_OFFSET, + IPI_BIT_MASK(remote)); +} diff --git a/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c new file mode 100644 index 0000000..434cd88 --- /dev/null +++ b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Top-level SMC handler for ZynqMP IPI Mailbox doorbell functions. + */ + +#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 "ipi_mailbox_svc.h" +#include <plat_ipi.h> +#include <plat_private.h> + +/********************************************************************* + * Macros definitions + ********************************************************************/ + +/* IPI SMC calls macros: */ +#define IPI_SMC_OPEN_IRQ_MASK 0x00000001U /* IRQ enable bit in IPI + * open SMC call + */ +#define IPI_SMC_NOTIFY_BLOCK_MASK 0x00000001U /* Flag to indicate if + * IPI notification needs + * to be blocking. + */ +#define IPI_SMC_ENQUIRY_DIRQ_MASK 0x00000001U /* Flag to indicate if + * notification interrupt + * to be disabled. + */ +#define IPI_SMC_ACK_EIRQ_MASK 0x00000001U /* Flag to indicate if + * notification interrupt + * to be enable. + */ + +#define UNSIGNED32_MASK 0xFFFFFFFFU /* 32bit mask */ + +/** + * ipi_smc_handler() - SMC handler for IPI SMC calls. + * @smc_fid: Function identifier. + * @x1: Arguments. + * @x2: Arguments. + * @x3: Arguments. + * @x4: Arguments. + * @cookie: Unused. + * @handle: Pointer to caller's context structure. + * @flags: SECURE_FLAG or NON_SECURE_FLAG. + * + * Return: Unused. + * + * Determines that smc_fid is valid and supported PM SMC Function ID from the + * list of pm_api_ids, otherwise completes the request with + * the unknown SMC Function ID. + * + * The SMC calls for PM service are forwarded from SIP Service SMC handler + * function with rt_svc_handle signature. + * + */ +uint64_t ipi_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, + uint64_t x3, uint64_t x4, const void *cookie, + void *handle, uint64_t flags) +{ + int32_t ret; + uint32_t ipi_local_id; + uint32_t ipi_remote_id; + uint32_t is_secure; + + ipi_local_id = x1 & UNSIGNED32_MASK; + ipi_remote_id = x2 & UNSIGNED32_MASK; + + /* OEN Number 48 to 63 is for Trusted App and OS + * GET_SMC_OEN limits the return value of OEN number to 63 by bitwise + * AND operation with 0x3F. + * Upper limit check for OEN value is not required. + */ + if (GET_SMC_OEN(smc_fid) >= OEN_TAP_START) { + is_secure = 1; + } else { + is_secure = 0; + } + + /* Validate IPI mailbox access */ + ret = ipi_mb_validate(ipi_local_id, ipi_remote_id, is_secure); + if (ret) + SMC_RET1(handle, ret); + + switch (GET_SMC_NUM(smc_fid)) { + case IPI_MAILBOX_OPEN: + ipi_mb_open(ipi_local_id, ipi_remote_id); + SMC_RET1(handle, 0); + case IPI_MAILBOX_RELEASE: + ipi_mb_release(ipi_local_id, ipi_remote_id); + SMC_RET1(handle, 0); + case IPI_MAILBOX_STATUS_ENQUIRY: + { + int32_t disable_irq; + + disable_irq = (x3 & IPI_SMC_ENQUIRY_DIRQ_MASK) ? 1 : 0; + ret = ipi_mb_enquire_status(ipi_local_id, ipi_remote_id); + if ((ret & IPI_MB_STATUS_RECV_PENDING) && disable_irq) + ipi_mb_disable_irq(ipi_local_id, ipi_remote_id); + SMC_RET1(handle, ret); + } + case IPI_MAILBOX_NOTIFY: + { + uint32_t is_blocking; + + is_blocking = (x3 & IPI_SMC_NOTIFY_BLOCK_MASK) ? 1 : 0; + ipi_mb_notify(ipi_local_id, ipi_remote_id, is_blocking); + SMC_RET1(handle, 0); + } + case IPI_MAILBOX_ACK: + { + int32_t enable_irq; + + enable_irq = (x3 & IPI_SMC_ACK_EIRQ_MASK) ? 1 : 0; + ipi_mb_ack(ipi_local_id, ipi_remote_id); + if (enable_irq) + ipi_mb_enable_irq(ipi_local_id, ipi_remote_id); + SMC_RET1(handle, 0); + } + case IPI_MAILBOX_ENABLE_IRQ: + ipi_mb_enable_irq(ipi_local_id, ipi_remote_id); + SMC_RET1(handle, 0); + case IPI_MAILBOX_DISABLE_IRQ: + ipi_mb_disable_irq(ipi_local_id, ipi_remote_id); + SMC_RET1(handle, 0); + default: + WARN("Unimplemented IPI service call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} diff --git a/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.h b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.h new file mode 100644 index 0000000..9198a98 --- /dev/null +++ b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* ZynqMP IPI mailbox doorbell service enums and defines */ + +#ifndef IPI_MAILBOX_SVC_H +#define IPI_MAILBOX_SVC_H + +#include <stdint.h> + +/********************************************************************* + * Enum definitions + ********************************************************************/ + +/* IPI SMC function numbers enum definition */ +enum ipi_api_id { + /* IPI mailbox operations functions: */ + IPI_MAILBOX_OPEN = 0x1000, + IPI_MAILBOX_RELEASE, + IPI_MAILBOX_STATUS_ENQUIRY, + IPI_MAILBOX_NOTIFY, + IPI_MAILBOX_ACK, + IPI_MAILBOX_ENABLE_IRQ, + IPI_MAILBOX_DISABLE_IRQ +}; + +/********************************************************************* + * IPI mailbox service APIs declarations + ********************************************************************/ + +/* IPI SMC handler */ +uint64_t ipi_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, + uint64_t x3, uint64_t x4, const void *cookie, void *handle, + uint64_t flags); + +#endif /* IPI_MAILBOX_SVC_H */ diff --git a/plat/xilinx/common/plat_console.c b/plat/xilinx/common/plat_console.c new file mode 100644 index 0000000..0c0e74b --- /dev/null +++ b/plat/xilinx/common/plat_console.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <common/debug.h> +#include <common/fdt_fixup.h> +#include <common/fdt_wrappers.h> +#include <drivers/arm/dcc.h> +#include <drivers/arm/pl011.h> +#include <drivers/cadence/cdns_uart.h> +#include <drivers/console.h> +#include <libfdt.h> +#include <plat_console.h> + +#include <platform_def.h> +#include <plat_private.h> + +static console_t console; + +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) +/** + * get_baudrate() - Get the baudrate form DTB. + * @dtb: Address of the Device Tree Blob (DTB). + * + * Return: On success returns the baudrate; on failure returns an error. + */ +static int32_t get_baudrate(void *dtb) +{ + int node; + int32_t ret = 0; + const char *prop, *path; + char *end; + int32_t baud_rate = 0; + + node = fdt_path_offset(dtb, "/secure-chosen"); + if (node < 0) { + node = fdt_path_offset(dtb, "/chosen"); + if (node < 0) { + ret = -FDT_ERR_NOTFOUND; + goto error; + } + } + + prop = fdt_getprop(dtb, node, "stdout-path", NULL); + if (prop == NULL) { + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + /* Parse string serial0:115200n8 */ + path = strchr(prop, ':'); + if (!path) { + ret = -FDT_ERR_NOTFOUND; + goto error; + } else { + + baud_rate = strtoul(path + 1, &end, 10); + if (baud_rate == 0 && end == path) { + ERROR("Conversion error occurred: %d\n", baud_rate); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + ret = baud_rate; + } + +error: + return ret; +} + +/** + * get_node_status() - Get the DTB node status. + * @dtb: Address of the Device Tree Blob (DTB). + * @node: Node address in the device tree. + * + * Return: On success, it returns 1; on failure, it returns an 0. + */ +static uint32_t get_node_status(void *dtb, int node) +{ + const char *status_cell; + uint32_t status = 0; + + status_cell = fdt_getprop(dtb, node, "status", NULL); + if (!status_cell || strcmp(status_cell, "okay") == 0) { + status = 1; + } else { + status = 0; + } + + return status; +} + +/** + * fdt_add_uart_info() - Add DTB information to a UART structure. + * @info: Pointer to the UART information structure. + * @node: Node address in the device tree. + * @dtb: Address of the Device Tree Blob(DTB). + * + * Return: On success, it returns 1; on failure, it returns an 0. + */ +static uint32_t fdt_add_uart_info(dt_uart_info_t *info, int node, void *dtb) +{ + uintptr_t base_addr; + const char *com; + uint32_t ret = 0; + + com = fdt_getprop(dtb, node, "compatible", NULL); + if (com != NULL) { + strlcpy(info->compatible, com, sizeof(info->compatible)); + } else { + ERROR("Compatible property not found in DTB node\n"); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + ret = fdt_get_reg_props_by_index(dtb, node, 0, &base_addr, NULL); + if (ret >= 0) { + info->base = base_addr; + } else { + ERROR("Failed to retrieve base address. Error code: %d\n", ret); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + info->status = get_node_status(dtb, node); + info->baud_rate = get_baudrate(dtb); + +error: + return ret; +} + +/** + * fdt_get_uart_info() - Get the uart information form DTB. + * @info: Pointer to the UART information structure. + * + * Return: On success, it returns 0; on failure, it returns an error+reason. + */ +static int fdt_get_uart_info(dt_uart_info_t *info) +{ + int node, ret = 0; + void *dtb = (void *)XILINX_OF_BOARD_DTB_ADDR; + + if (fdt_check_header(dtb) != 0) { + ERROR("Can't read DT at %p\n", dtb); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + ret = fdt_open_into(dtb, dtb, XILINX_OF_BOARD_DTB_MAX_SIZE); + if (ret < 0) { + ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + node = fdt_get_stdout_node_offset(dtb); + if (node < 0) { + ERROR("DT get stdout node failed : %d\n", node); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + + ret = fdt_add_uart_info(info, node, dtb); + if (ret < 0) { + ERROR("Failed to add DT UART info: %d\n", ret); + ret = -FDT_ERR_NOTFOUND; + goto error; + } + +error: + return ret; +} + +/** + * check_fdt_uart_info() - Check early uart info with DTB uart info. + * @info: Pointer to the UART information structure. + * + * Return: On success, it returns 0; on failure, it returns an error+reason. + */ +static int check_fdt_uart_info(dt_uart_info_t *info) +{ + uint32_t ret = 0; + + if (info->status == 0) { + ret = -ENODEV; + goto error; + } + + if ((info->base == console.base) && + (info->baud_rate == UART_BAUDRATE) && !CONSOLE_IS(dcc)) { + ret = -ENODEV; + goto error; + } + +error: + return ret; +} + +/** + * console_boot_end() - Unregister the console_t instance form the console list. + * @boot_console: Pointer to the console information structure. + */ +static void console_boot_end(console_t *boot_console) +{ + if (CONSOLE_IS(dcc)) { + console_dcc_unregister(); + } else { + console_flush(); + (void)console_unregister(boot_console); + } +} + +/** + * setup_runtime_console() - Registers the runtime uart with console list. + * @clock: UART clock. + * @info: Pointer to the UART information structure. + */ +static void setup_runtime_console(uint32_t clock, dt_uart_info_t *info) +{ + static console_t bl31_runtime_console; + uint32_t rc; + +#if defined(PLAT_zynqmp) + rc = console_cdns_register(info->base, + clock, + info->baud_rate, + &bl31_runtime_console); +#else + rc = console_pl011_register(info->base, + clock, + info->baud_rate, + &bl31_runtime_console); +#endif + if (rc == 0) { + panic(); + } + + console_set_scope(&bl31_runtime_console, + CONSOLE_FLAG_BOOT | CONSOLE_FLAG_RUNTIME | + CONSOLE_FLAG_CRASH); +} + + +/** + * runtime_console_init() - Initializes the run time console information. + * @uart_info: Pointer to the UART information structure. + * @bl31_boot_console: Pointer to the console information structure. + * @clock: UART clock. + * + * Return: On success, it returns 0; on failure, it returns an error+reason; + */ +static int32_t runtime_console_init(dt_uart_info_t *uart_info, + console_t *bl31_boot_console, + uint32_t clock) +{ + int32_t rc = 0; + + /* Parse UART information from Device Tree Blob (DTB) */ + rc = fdt_get_uart_info(uart_info); + if (rc < 0) { + rc = -FDT_ERR_NOTFOUND; + } + + if (strncmp(uart_info->compatible, DT_UART_COMPAT, + strlen(DT_UART_COMPAT)) == 0) { + + if (check_fdt_uart_info(uart_info) == 0) { + setup_runtime_console(clock, uart_info); + console_boot_end(bl31_boot_console); + INFO("Runtime console setup\n"); + } else { + INFO("Early console and DTB console are same\n"); + } + } else if (strncmp(uart_info->compatible, DT_UART_DCC_COMPAT, + strlen(DT_UART_DCC_COMPAT)) == 0) { + rc = console_dcc_register(); + if (rc == 0) { + panic(); + } + console_boot_end(bl31_boot_console); + } else { + WARN("BL31: No console device found in DT.\n"); + } + + return rc; +} +#endif + +void setup_console(void) +{ + uint32_t rc; + uint32_t uart_clk = get_uart_clk(); + +#if defined(PLAT_zynqmp) + if (CONSOLE_IS(cadence) || (CONSOLE_IS(cadence1))) { + rc = console_cdns_register(UART_BASE, + uart_clk, + UART_BAUDRATE, + &console); + if (rc == 0) { + panic(); + } + + console_set_scope(&console, CONSOLE_FLAG_BOOT | + CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH); + } +#else + if (CONSOLE_IS(pl011) || (CONSOLE_IS(pl011_1))) { + /* Initialize the console to provide early debug support */ + rc = console_pl011_register((uint32_t)UART_BASE, + uart_clk, + (uint32_t)UART_BAUDRATE, + &console); + if (rc == 0) { + panic(); + } + + console_set_scope(&console, CONSOLE_FLAG_BOOT | + CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH); + } +#endif + if (CONSOLE_IS(dcc)) { + /* Initialize the dcc console for debug */ + rc = console_dcc_register(); + if (rc == 0) { + panic(); + } + } + INFO("BL31: Early console setup\n"); + +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) + static dt_uart_info_t uart_info = {0}; + + /* Initialize the runtime console using UART information from the DTB */ + rc = runtime_console_init(&uart_info, &console, uart_clk); + if (rc < 0) { + ERROR("Failed to initialize runtime console: %d\n", rc); + } +#endif +} diff --git a/plat/xilinx/common/plat_fdt.c b/plat/xilinx/common/plat_fdt.c new file mode 100644 index 0000000..de5d1a1 --- /dev/null +++ b/plat/xilinx/common/plat_fdt.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#include <common/debug.h> +#include <common/fdt_fixup.h> +#include <common/fdt_wrappers.h> +#include <libfdt.h> +#include <lib/xlat_tables/xlat_tables_v2.h> + +#include <plat_fdt.h> +#include <platform_def.h> + +void prepare_dtb(void) +{ +#if defined(XILINX_OF_BOARD_DTB_ADDR) + void *dtb; + int map_ret = 0; + int ret = 0; + + dtb = (void *)XILINX_OF_BOARD_DTB_ADDR; + + if (!IS_TFA_IN_OCM(BL31_BASE)) { + +#if defined(PLAT_XLAT_TABLES_DYNAMIC) + map_ret = mmap_add_dynamic_region((unsigned long long)dtb, + (uintptr_t)dtb, + XILINX_OF_BOARD_DTB_MAX_SIZE, + MT_MEMORY | MT_RW | MT_NS); + if (map_ret != 0) { + WARN("Failed to add dynamic region for dtb: error %d\n", + map_ret); + } +#endif + + if (!map_ret) { + /* Return if no device tree is detected */ + if (fdt_check_header(dtb) != 0) { + NOTICE("Can't read DT at %p\n", dtb); + } else { + ret = fdt_open_into(dtb, dtb, XILINX_OF_BOARD_DTB_MAX_SIZE); + + if (ret < 0) { + ERROR("Invalid Device Tree at %p: error %d\n", + dtb, ret); + } else { + + if (dt_add_psci_node(dtb)) { + WARN("Failed to add PSCI Device Tree node\n"); + } + + if (dt_add_psci_cpu_enable_methods(dtb)) { + WARN("Failed to add PSCI cpu enable methods in DT\n"); + } + + /* Reserve memory used by Trusted Firmware. */ + ret = fdt_add_reserved_memory(dtb, + "tf-a", + BL31_BASE, + BL31_LIMIT + - + BL31_BASE); + if (ret < 0) { + WARN("Failed to add reserved memory nodes for BL31 to DT.\n"); + } + + ret = fdt_pack(dtb); + if (ret < 0) { + WARN("Failed to pack dtb at %p: error %d\n", + dtb, ret); + } + flush_dcache_range((uintptr_t)dtb, + fdt_blob_size(dtb)); + + INFO("Changed device tree to advertise PSCI and reserved memories.\n"); + + } + } + + } + + +#if defined(PLAT_XLAT_TABLES_DYNAMIC) + if (!map_ret) { + ret = mmap_remove_dynamic_region((uintptr_t)dtb, + XILINX_OF_BOARD_DTB_MAX_SIZE); + if (ret != 0) { + WARN("Failed to remove dynamic region for dtb:error %d\n", + ret); + } + } +#endif + } + +#endif +} diff --git a/plat/xilinx/common/plat_startup.c b/plat/xilinx/common/plat_startup.c new file mode 100644 index 0000000..5beb765 --- /dev/null +++ b/plat/xilinx/common/plat_startup.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2014-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <inttypes.h> +#include <stdint.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <plat_startup.h> + + +/* + * HandoffParams + * Parameter bitfield encoding + * ----------------------------------------------------------------------------- + * Exec State 0 0 -> Aarch64, 1-> Aarch32 + * endianness 1 0 -> LE, 1 -> BE + * secure (TZ) 2 0 -> Non secure, 1 -> secure + * EL 3:4 00 -> EL0, 01 -> EL1, 10 -> EL2, 11 -> EL3 + * CPU# 5:6 00 -> A53_0, 01 -> A53_1, 10 -> A53_2, 11 -> A53_3 + * Reserved 7:10 Reserved + * Cluster# 11:12 00 -> Cluster 0, 01 -> Cluster 1, 10 -> Cluster 2, + * 11 -> Cluster (Applicable for Versal NET only). + * Reserved 13:16 Reserved + */ + +#define XBL_FLAGS_ESTATE_SHIFT 0U +#define XBL_FLAGS_ESTATE_MASK (1U << XBL_FLAGS_ESTATE_SHIFT) +#define XBL_FLAGS_ESTATE_A64 0U +#define XBL_FLAGS_ESTATE_A32 1U + +#define XBL_FLAGS_ENDIAN_SHIFT 1U +#define XBL_FLAGS_ENDIAN_MASK (1U << XBL_FLAGS_ENDIAN_SHIFT) +#define XBL_FLAGS_ENDIAN_LE 0U +#define XBL_FLAGS_ENDIAN_BE 1U + +#define XBL_FLAGS_TZ_SHIFT 2U +#define XBL_FLAGS_TZ_MASK (1U << XBL_FLAGS_TZ_SHIFT) +#define XBL_FLAGS_NON_SECURE 0U +#define XBL_FLAGS_SECURE 1U + +#define XBL_FLAGS_EL_SHIFT 3U +#define XBL_FLAGS_EL_MASK (3U << XBL_FLAGS_EL_SHIFT) +#define XBL_FLAGS_EL0 0U +#define XBL_FLAGS_EL1 1U +#define XBL_FLAGS_EL2 2U +#define XBL_FLAGS_EL3 3U + +#define XBL_FLAGS_CPU_SHIFT 5U +#define XBL_FLAGS_CPU_MASK (3U << XBL_FLAGS_CPU_SHIFT) +#define XBL_FLAGS_A53_0 0U +#define XBL_FLAGS_A53_1 1U +#define XBL_FLAGS_A53_2 2U +#define XBL_FLAGS_A53_3 3U + +#if defined(PLAT_versal_net) +#define XBL_FLAGS_CLUSTER_SHIFT 11U +#define XBL_FLAGS_CLUSTER_MASK GENMASK(11, 12) + +#define XBL_FLAGS_CLUSTER_0 0U +#endif /* PLAT_versal_net */ + +/** + * get_xbl_cpu() - Get the target CPU for partition. + * @partition: Pointer to partition struct. + * + * Return: XBL_FLAGS_A53_0, XBL_FLAGS_A53_1, XBL_FLAGS_A53_2 or XBL_FLAGS_A53_3. + * + */ +static int32_t get_xbl_cpu(const struct xbl_partition *partition) +{ + uint64_t flags = partition->flags & XBL_FLAGS_CPU_MASK; + + return flags >> XBL_FLAGS_CPU_SHIFT; +} + +/** + * get_xbl_el() - Get the target exception level for partition. + * @partition: Pointer to partition struct. + * + * Return: XBL_FLAGS_EL0, XBL_FLAGS_EL1, XBL_FLAGS_EL2 or XBL_FLAGS_EL3. + * + */ +static int32_t get_xbl_el(const struct xbl_partition *partition) +{ + uint64_t flags = partition->flags & XBL_FLAGS_EL_MASK; + + return flags >> XBL_FLAGS_EL_SHIFT; +} + +/** + * get_xbl_ss() - Get the target security state for partition. + * @partition: Pointer to partition struct. + * + * Return: XBL_FLAGS_NON_SECURE or XBL_FLAGS_SECURE. + * + */ +static int32_t get_xbl_ss(const struct xbl_partition *partition) +{ + uint64_t flags = partition->flags & XBL_FLAGS_TZ_MASK; + + return flags >> XBL_FLAGS_TZ_SHIFT; +} + +/** + * get_xbl_endian() - Get the target endianness for partition. + * @partition: Pointer to partition struct. + * + * Return: SPSR_E_LITTLE or SPSR_E_BIG. + * + */ +static int32_t get_xbl_endian(const struct xbl_partition *partition) +{ + uint64_t flags = partition->flags & XBL_FLAGS_ENDIAN_MASK; + + flags >>= XBL_FLAGS_ENDIAN_SHIFT; + + if (flags == XBL_FLAGS_ENDIAN_BE) { + return SPSR_E_BIG; + } else { + return SPSR_E_LITTLE; + } +} + +/** + * get_xbl_estate() - Get the target execution state for partition. + * @partition: Pointer to partition struct. + * + * Return: XBL_FLAGS_ESTATE_A32 or XBL_FLAGS_ESTATE_A64. + * + */ +static int32_t get_xbl_estate(const struct xbl_partition *partition) +{ + uint64_t flags = partition->flags & XBL_FLAGS_ESTATE_MASK; + + return flags >> XBL_FLAGS_ESTATE_SHIFT; +} + +#if defined(PLAT_versal_net) +/** + * get_xbl_cluster - Get the cluster number + * @partition: pointer to the partition structure. + * + * Return: cluster number for the partition. + */ +static int32_t get_xbl_cluster(const struct xbl_partition *partition) +{ + uint64_t flags = partition->flags & XBL_FLAGS_CLUSTER_MASK; + + return (int32_t)(flags >> XBL_FLAGS_CLUSTER_SHIFT); +} +#endif /* PLAT_versal_net */ + +/** + * xbl_handover() - Populates the bl32 and bl33 image info structures. + * @bl32: BL32 image info structure. + * @bl33: BL33 image info structure. + * @handoff_addr: TF-A handoff address. + * + * Process the handoff parameters from the XBL and populate the BL32 and BL33 + * image info structures accordingly. + * + * Return: Return the status of the handoff. The value will be from the + * xbl_handoff enum. + * + */ +enum xbl_handoff xbl_handover(entry_point_info_t *bl32, + entry_point_info_t *bl33, + uint64_t handoff_addr) +{ + const struct xbl_handoff_params *HandoffParams; + + if (!handoff_addr) { + WARN("BL31: No handoff structure passed\n"); + return XBL_HANDOFF_NO_STRUCT; + } + + HandoffParams = (struct xbl_handoff_params *)handoff_addr; + if ((HandoffParams->magic[0] != 'X') || + (HandoffParams->magic[1] != 'L') || + (HandoffParams->magic[2] != 'N') || + (HandoffParams->magic[3] != 'X')) { + ERROR("BL31: invalid handoff structure at %" PRIx64 "\n", handoff_addr); + return XBL_HANDOFF_INVAL_STRUCT; + } + + VERBOSE("BL31: TF-A handoff params at:0x%" PRIx64 ", entries:%u\n", + handoff_addr, HandoffParams->num_entries); + if (HandoffParams->num_entries > XBL_MAX_PARTITIONS) { + ERROR("BL31: TF-A handoff params: too many partitions (%u/%u)\n", + HandoffParams->num_entries, XBL_MAX_PARTITIONS); + return XBL_HANDOFF_TOO_MANY_PARTS; + } + + /* + * we loop over all passed entries but only populate two image structs + * (bl32, bl33). I.e. the last applicable images in the handoff + * structure will be used for the hand off + */ + for (size_t i = 0; i < HandoffParams->num_entries; i++) { + entry_point_info_t *image; + int32_t target_estate, target_secure, target_cpu; + uint32_t target_endianness, target_el; + + VERBOSE("BL31: %zd: entry:0x%" PRIx64 ", flags:0x%" PRIx64 "\n", i, + HandoffParams->partition[i].entry_point, + HandoffParams->partition[i].flags); + +#if defined(PLAT_versal_net) + uint32_t target_cluster; + + target_cluster = get_xbl_cluster(&HandoffParams->partition[i]); + if (target_cluster != XBL_FLAGS_CLUSTER_0) { + WARN("BL31: invalid target Cluster (%i)\n", + target_cluster); + continue; + } +#endif /* PLAT_versal_net */ + + target_cpu = get_xbl_cpu(&HandoffParams->partition[i]); + if (target_cpu != XBL_FLAGS_A53_0) { + WARN("BL31: invalid target CPU (%i)\n", target_cpu); + continue; + } + + target_el = get_xbl_el(&HandoffParams->partition[i]); + if ((target_el == XBL_FLAGS_EL3) || + (target_el == XBL_FLAGS_EL0)) { + WARN("BL31: invalid target exception level(%i)\n", + target_el); + continue; + } + + target_secure = get_xbl_ss(&HandoffParams->partition[i]); + if (target_secure == XBL_FLAGS_SECURE && + target_el == XBL_FLAGS_EL2) { + WARN("BL31: invalid security state (%i) for exception level (%i)\n", + target_secure, target_el); + continue; + } + + target_estate = get_xbl_estate(&HandoffParams->partition[i]); + target_endianness = get_xbl_endian(&HandoffParams->partition[i]); + + if (target_secure == XBL_FLAGS_SECURE) { + image = bl32; + + if (target_estate == XBL_FLAGS_ESTATE_A32) { + bl32->spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM, + target_endianness, + DISABLE_ALL_EXCEPTIONS); + } else { + bl32->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); + } + } else { + image = bl33; + + if (target_estate == XBL_FLAGS_ESTATE_A32) { + if (target_el == XBL_FLAGS_EL2) { + target_el = MODE32_hyp; + } else { + target_el = MODE32_sys; + } + + bl33->spsr = SPSR_MODE32(target_el, SPSR_T_ARM, + target_endianness, + DISABLE_ALL_EXCEPTIONS); + } else { + if (target_el == XBL_FLAGS_EL2) { + target_el = MODE_EL2; + } else { + target_el = MODE_EL1; + } + + bl33->spsr = SPSR_64(target_el, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); + } + } + + VERBOSE("Setting up %s entry point to:%" PRIx64 ", el:%x\n", + target_secure == XBL_FLAGS_SECURE ? "BL32" : "BL33", + HandoffParams->partition[i].entry_point, + target_el); + image->pc = HandoffParams->partition[i].entry_point; + + if (target_endianness == SPSR_E_BIG) { + EP_SET_EE(image->h.attr, EP_EE_BIG); + } else { + EP_SET_EE(image->h.attr, EP_EE_LITTLE); + } + } + + return XBL_HANDOFF_SUCCESS; +} diff --git a/plat/xilinx/common/pm_service/pm_api_sys.c b/plat/xilinx/common/pm_service/pm_api_sys.c new file mode 100644 index 0000000..ffc39bb --- /dev/null +++ b/plat/xilinx/common/pm_service/pm_api_sys.c @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Versal system level PM-API functions and communication with PMC via + * IPI interrupts + */ + +#include <drivers/arm/gic_common.h> +#include <lib/mmio.h> +#include <lib/utils.h> +#include <plat/common/platform.h> +#include <platform_def.h> +#include <pm_api_sys.h> +#include <pm_client.h> +#include <pm_common.h> +#include <pm_defs.h> +#include <pm_ipi.h> +#include "pm_svc_main.h" + +#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5U) + 1U) + +/* default shutdown/reboot scope is system(2) */ +static uint32_t pm_shutdown_scope = XPM_SHUTDOWN_SUBTYPE_RST_SYSTEM; + +/** + * pm_get_shutdown_scope() - Get the currently set shutdown scope. + * + * Return: Shutdown scope value. + * + */ +uint32_t pm_get_shutdown_scope(void) +{ + return pm_shutdown_scope; +} + +/* PM API functions */ + +/** + * pm_client_set_wakeup_sources - Set all devices with enabled interrupts as + * wake sources in the XilPM. + * @node_id: Node id of processor. + * + */ +void pm_client_set_wakeup_sources(uint32_t node_id) +{ + uint32_t reg_num, device_id; + uint8_t pm_wakeup_nodes_set[XPM_NODEIDX_DEV_MAX] = {0U}; + uint32_t isenabler1 = PLAT_GICD_BASE_VALUE + GICD_ISENABLER + 4U; + + zeromem(&pm_wakeup_nodes_set, (u_register_t)sizeof(pm_wakeup_nodes_set)); + + for (reg_num = 0U; reg_num < NUM_GICD_ISENABLER; reg_num++) { + uint32_t base_irq = reg_num << ISENABLER_SHIFT; + uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2)); + + if (reg == 0U) { + continue; + } + + while (reg != 0U) { + enum pm_device_node_idx node_idx; + uint32_t idx, irq, lowest_set = reg & (-reg); + enum pm_ret_status ret; + + idx = (uint32_t)__builtin_ctz(lowest_set); + irq = base_irq + idx; + + if (irq > IRQ_MAX) { + break; + } + + node_idx = irq_to_pm_node_idx(irq); + reg &= ~lowest_set; + + if (node_idx > XPM_NODEIDX_DEV_MIN) { + if (pm_wakeup_nodes_set[node_idx] == 0U) { + /* Get device ID from node index */ + device_id = PERIPH_DEVID((uint32_t)node_idx); + ret = pm_set_wakeup_source(node_id, + device_id, 1U, + SECURE_FLAG); + pm_wakeup_nodes_set[node_idx] = (ret == PM_RET_SUCCESS) ? + 1U : 0U; + } + } + } + } +} + +/** + * pm_handle_eemi_call() - PM call for processor to send eemi payload. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * @x0: Arguments received per SMC64 standard. + * @x1: Arguments received per SMC64 standard. + * @x2: Arguments received per SMC64 standard. + * @x3: Arguments received per SMC64 standard. + * @x4: Arguments received per SMC64 standard. + * @x5: Arguments received per SMC64 standard. + * @result: Payload received from firmware. + * + * Return: PM_RET_SUCCESS on success or error code. + * + */ +enum pm_ret_status pm_handle_eemi_call(uint32_t flag, uint32_t x0, uint32_t x1, + uint32_t x2, uint32_t x3, uint32_t x4, + uint32_t x5, uint64_t *result) +{ + uint32_t payload[PAYLOAD_ARG_CNT] = {0}; + uint32_t module_id; + + module_id = (x0 & MODULE_ID_MASK) >> 8U; + + //default module id is for LIBPM + if (module_id == 0) { + module_id = LIBPM_MODULE_ID; + } + + PM_PACK_PAYLOAD6(payload, module_id, flag, x0, x1, x2, x3, x4, x5); + return pm_ipi_send_sync(primary_proc, payload, (uint32_t *)result, PAYLOAD_ARG_CNT); +} + +/** + * pm_self_suspend() - PM call for processor to suspend itself + * @nid: Node id of the processor or subsystem. + * @latency: Requested maximum wakeup latency (not supported). + * @state: Requested state. + * @address: Resume address. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This is a blocking call, it will return only once PMU has responded. + * On a wakeup, resume address will be automatically set by PMU. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_self_suspend(uint32_t nid, + uint32_t latency, + uint32_t state, + uintptr_t address, uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + uint32_t cpuid = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpuid); + + if (proc == NULL) { + WARN("Failed to get proc %d\n", cpuid); + return PM_RET_ERROR_INTERNAL; + } + + /* + * Do client specific suspend operations + * (e.g. set powerdown request bit) + */ + pm_client_suspend(proc, state); + + /* Send request to the PLM */ + PM_PACK_PAYLOAD6(payload, LIBPM_MODULE_ID, flag, PM_SELF_SUSPEND, + proc->node_id, latency, state, address, + (address >> 32)); + return pm_ipi_send_sync(proc, payload, NULL, 0); +} + +/** + * pm_abort_suspend() - PM call to announce that a prior suspend request + * is to be aborted. + * @reason: Reason for the abort. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * Calling PU expects the PMU to abort the initiated suspend procedure. + * This is a non-blocking call without any acknowledge. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason, uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* + * Do client specific abort suspend operations + * (e.g. enable interrupts and clear powerdown request bit) + */ + pm_client_abort_suspend(); + + /* Send request to the PLM */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_ABORT_SUSPEND, + reason, primary_proc->node_id); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_req_suspend() - PM call to request for another PU or subsystem to + * be suspended gracefully. + * @target: Node id of the targeted PU or subsystem. + * @ack: Flag to specify whether acknowledge is requested. + * @latency: Requested wakeup latency (not supported) + * @state: Requested state (not supported). + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_req_suspend(uint32_t target, uint8_t ack, + uint32_t latency, uint32_t state, + uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_REQ_SUSPEND, target, + latency, state); + if (ack == IPI_BLOCKING) { + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + } else { + return pm_ipi_send(primary_proc, payload); + } +} + +/** + * pm_req_wakeup() - PM call for processor to wake up selected processor + * or subsystem. + * @target: Device ID of the processor or subsystem to wake up. + * @set_address: Resume address presence indicator. + * 1 - resume address specified, 0 - otherwise. + * @address: Resume address. + * @ack: Flag to specify whether acknowledge requested. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This API function is either used to power up another APU core for SMP + * (by PSCI) or to power up an entirely different PU or subsystem, such + * as RPU0, RPU, or PL_CORE_xx. Resume address for the target PU will be + * automatically set by PMC. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_req_wakeup(uint32_t target, uint32_t set_address, + uintptr_t address, uint8_t ack, uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC to perform the wake of the PU */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, flag, PM_REQ_WAKEUP, target, + set_address, address, ack); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_get_callbackdata() - Read from IPI response buffer. + * @data: array of PAYLOAD_ARG_CNT elements. + * @count: Number of values to return. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * @ack: 0 - Do not ack IPI after reading payload. + * 1 - Ack IPI after reading payload. + * + * Read value from ipi buffer response buffer. + * Return: Returns status, either success or error. + * + */ +enum pm_ret_status pm_get_callbackdata(uint32_t *data, size_t count, uint32_t flag, uint32_t ack) +{ + enum pm_ret_status ret = PM_RET_SUCCESS; + /* Return if interrupt is not from PMU */ + if (pm_ipi_irq_status(primary_proc) == 0) { + return ret; + } + + ret = pm_ipi_buff_read_callb(data, count); + + if (ack != 0U) { + pm_ipi_irq_clear(primary_proc); + } + + return ret; +} + +/** + * pm_pll_set_param() - Set PLL parameter. + * @clk_id: PLL clock ID. + * @param: PLL parameter ID. + * @value: Value to set for PLL parameter. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This API is deprecated and maintained here for backward compatibility. + * New use of this API should be avoided for versal platform. + * This API and its use cases will be removed for versal platform. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_pll_set_param(uint32_t clk_id, uint32_t param, + uint32_t value, uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_PLL_SET_PARAMETER, + clk_id, param, value); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pll_get_param() - Get PLL parameter value. + * @clk_id: PLL clock ID. + * @param: PLL parameter ID. + * @value: Buffer to store PLL parameter value. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This API is deprecated and maintained here for backward compatibility. + * New use of this API should be avoided for versal platform. + * This API and its use cases will be removed for versal platform. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_pll_get_param(uint32_t clk_id, uint32_t param, + uint32_t *value, uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_PLL_GET_PARAMETER, + clk_id, param); + + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/** + * pm_pll_set_mode() - Set PLL mode. + * @clk_id: PLL clock ID. + * @mode: PLL mode. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This API is deprecated and maintained here for backward compatibility. + * New use of this API should be avoided for versal platform. + * This API and its use cases will be removed for versal platform. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_pll_set_mode(uint32_t clk_id, uint32_t mode, + uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_PLL_SET_MODE, + clk_id, mode); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pll_get_mode() - Get PLL mode. + * @clk_id: PLL clock ID. + * @mode: Buffer to store PLL mode. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This API is deprecated and maintained here for backward compatibility. + * New use of this API should be avoided for versal platform. + * This API and its use cases will be removed for versal platform. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_pll_get_mode(uint32_t clk_id, uint32_t *mode, + uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, flag, PM_PLL_GET_MODE, + clk_id); + + return pm_ipi_send_sync(primary_proc, payload, mode, 1); +} + +/** + * pm_force_powerdown() - PM call to request for another PU or subsystem to + * be powered down forcefully. + * @target: Device ID of the PU node to be forced powered down. + * @ack: Flag to specify whether acknowledge is requested + * @flag: 0 - Call from secure source + * 1 - Call from non-secure source + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_force_powerdown(uint32_t target, uint8_t ack, + uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_FORCE_POWERDOWN, + target, ack); + + if (ack == IPI_BLOCKING) { + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + } else { + return pm_ipi_send(primary_proc, payload); + } +} + +/** + * pm_system_shutdown() - PM call to request a system shutdown or restart. + * @type: Shutdown or restart? 0=shutdown, 1=restart, 2=setscope. + * @subtype: Scope: 0=APU-subsystem, 1=PS, 2=system. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype, + uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + if (type == XPM_SHUTDOWN_TYPE_SETSCOPE_ONLY) { + /* Setting scope for subsequent PSCI reboot or shutdown */ + pm_shutdown_scope = subtype; + return PM_RET_SUCCESS; + } + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, flag, PM_SYSTEM_SHUTDOWN, + type, subtype); + + return pm_ipi_send_non_blocking(primary_proc, payload); +} + +/** + * pm_query_data() - PM API for querying firmware data. + * @qid: The type of data to query. + * @arg1: Argument 1 to requested query data call. + * @arg2: Argument 2 to requested query data call. + * @arg3: Argument 3 to requested query data call. + * @data: Returned output data. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This API is deprecated and maintained here for backward compatibility. + * New use of this API should be avoided for versal platform. + * This API and its use cases will be removed for versal platform. + * + * Return: 0 if success else non-zero error code of type + * enum pm_ret_status. + * + */ +enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2, + uint32_t arg3, uint32_t *data, uint32_t flag) +{ + uint32_t ret; + uint32_t version[PAYLOAD_ARG_CNT] = {0}; + uint32_t payload[PAYLOAD_ARG_CNT]; + uint32_t fw_api_version; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, flag, PM_QUERY_DATA, qid, + arg1, arg2, arg3); + + ret = pm_feature_check((uint32_t)PM_QUERY_DATA, &version[0], flag); + if (ret == PM_RET_SUCCESS) { + fw_api_version = version[0] & 0xFFFFU; + if ((fw_api_version == 2U) && + ((qid == XPM_QID_CLOCK_GET_NAME) || + (qid == XPM_QID_PINCTRL_GET_FUNCTION_NAME))) { + ret = pm_ipi_send_sync(primary_proc, payload, data, PAYLOAD_ARG_CNT); + if (ret == PM_RET_SUCCESS) { + ret = data[0]; + data[0] = data[1]; + data[1] = data[2]; + data[2] = data[3]; + } + } else { + ret = pm_ipi_send_sync(primary_proc, payload, data, PAYLOAD_ARG_CNT); + } + } + return ret; +} +/** + * pm_api_ioctl() - PM IOCTL API for device control and configs. + * @device_id: Device ID. + * @ioctl_id: ID of the requested IOCTL. + * @arg1: Argument 1 to requested IOCTL call. + * @arg2: Argument 2 to requested IOCTL call. + * @arg3: Argument 3 to requested IOCTL call. + * @value: Returned output value. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * This API is deprecated and maintained here for backward compatibility. + * New use of this API should be avoided for versal platform. + * This API and its use cases will be removed for versal platform. + * + * This function calls IOCTL to firmware for device control and configuration. + * + * Return: Returns status, either 0 on success or non-zero error code + * of type enum pm_ret_status. + * + */ +enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id, + uint32_t arg1, uint32_t arg2, uint32_t arg3, + uint32_t *value, uint32_t flag) +{ + enum pm_ret_status ret; + + switch (ioctl_id) { + case IOCTL_SET_PLL_FRAC_MODE: + ret = pm_pll_set_mode(arg1, arg2, flag); + break; + case IOCTL_GET_PLL_FRAC_MODE: + ret = pm_pll_get_mode(arg1, value, flag); + break; + case IOCTL_SET_PLL_FRAC_DATA: + ret = pm_pll_set_param(arg1, (uint32_t)PM_PLL_PARAM_DATA, arg2, flag); + break; + case IOCTL_GET_PLL_FRAC_DATA: + ret = pm_pll_get_param(arg1, (uint32_t)PM_PLL_PARAM_DATA, value, flag); + break; + case IOCTL_SET_SGI: + /* Get the sgi number */ + ret = pm_register_sgi(arg1, arg2); + if (ret != 0) { + return PM_RET_ERROR_ARGS; + } + ret = PM_RET_SUCCESS; + break; + default: + return PM_RET_ERROR_NOTSUPPORTED; + } + + return ret; +} + +/** + * pm_set_wakeup_source() - PM call to specify the wakeup source while + * suspended. + * @target: Device id of the targeted PU or subsystem + * @wkup_device: Device id of the wakeup peripheral + * @enable: Enable or disable the specified peripheral as wake source + * @flag: 0 - Call from secure source + * 1 - Call from non-secure source + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t wkup_device, + uint8_t enable, uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, flag, PM_SET_WAKEUP_SOURCE, + target, wkup_device, enable); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_feature_check() - Returns the supported API version if supported. + * @api_id: API ID to check. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * @ret_payload: pointer to array of PAYLOAD_ARG_CNT number of + * words Returned supported API version and bitmasks + * for IOCTL and QUERY ID + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *ret_payload, + uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + uint32_t module_id; + + /* Return version of API which are implemented in TF-A only */ + switch (api_id) { + case PM_GET_CALLBACK_DATA: + case PM_GET_TRUSTZONE_VERSION: + ret_payload[0] = PM_API_VERSION_2; + return PM_RET_SUCCESS; + case TF_A_PM_REGISTER_SGI: + ret_payload[0] = PM_API_BASE_VERSION; + return PM_RET_SUCCESS; + default: + break; + } + + module_id = (api_id & MODULE_ID_MASK) >> 8U; + + /* + * feature check should be done only for LIBPM module + * If module_id is 0, then we consider it LIBPM module as default id + */ + if ((module_id > 0) && (module_id != LIBPM_MODULE_ID)) { + return PM_RET_SUCCESS; + } + + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, flag, + PM_FEATURE_CHECK, api_id); + return pm_ipi_send_sync(primary_proc, payload, ret_payload, PAYLOAD_ARG_CNT); +} + +/** + * pm_load_pdi() - Load the PDI. This function provides support to load + * PDI from linux. + * + * @src: Source device of pdi(DDR, OCM, SD etc). + * @address_low: lower 32-bit Linear memory space address. + * @address_high: higher 32-bit Linear memory space address. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_load_pdi(uint32_t src, uint32_t address_low, + uint32_t address_high, uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, LOADER_MODULE_ID, flag, PM_LOAD_PDI, src, + address_high, address_low); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_register_notifier() - PM call to register a subsystem to be notified + * about the device event. + * @device_id: Device ID for the Node to which the event is related. + * @event: Event in question. + * @wake: Wake subsystem upon capturing the event if value 1. + * @enable: Enable the registration for value 1, disable for value 0. + * @flag: 0 - Call from secure source. + * 1 - Call from non-secure source. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_register_notifier(uint32_t device_id, uint32_t event, + uint32_t wake, uint32_t enable, + uint32_t flag) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, flag, PM_REGISTER_NOTIFIER, + device_id, event, wake, enable); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_get_chipid() - Read silicon ID registers. + * @value: Buffer for two 32bit words. + * + * Return: Returns status, either success or error+reason and, + * optionally, @value. + */ +enum pm_ret_status pm_get_chipid(uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD1(payload, LIBPM_MODULE_ID, SECURE_FLAG, PM_GET_CHIPID); + + return pm_ipi_send_sync(primary_proc, payload, value, 2); +} diff --git a/plat/xilinx/common/pm_service/pm_ipi.c b/plat/xilinx/common/pm_service/pm_ipi.c new file mode 100644 index 0000000..56567dd --- /dev/null +++ b/plat/xilinx/common/pm_service/pm_ipi.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include <arch_helpers.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> +#include <plat/common/platform.h> + +#include <ipi.h> +#include <plat_ipi.h> +#include <plat_private.h> +#include "pm_defs.h" +#include "pm_ipi.h" + +#define ERROR_CODE_MASK (0xFFFFU) +#define PM_OFFSET (0U) + +/* + * ARM v8.2, the cache will turn off automatically when cpu + * power down. Therefore, there is no doubt to use the spin_lock here. + */ +#if !HW_ASSISTED_COHERENCY +DEFINE_BAKERY_LOCK(pm_secure_lock); +static inline void pm_ipi_lock_get(void) +{ + bakery_lock_get(&pm_secure_lock); +} + +static inline void pm_ipi_lock_release(void) +{ + bakery_lock_release(&pm_secure_lock); +} +#else +spinlock_t pm_secure_lock; +static inline void pm_ipi_lock_get(void) +{ + spin_lock(&pm_secure_lock); +} + +static inline void pm_ipi_lock_release(void) +{ + spin_unlock(&pm_secure_lock); +} +#endif + +/** + * pm_ipi_init() - Initialize IPI peripheral for communication with + * remote processor. + * @proc: Pointer to the processor who is initiating request. + * + * Return: On success, the initialization function must return 0. + * Any other return value will cause the framework to ignore + * the service. + * + * Called from pm_setup initialization function. + */ +void pm_ipi_init(const struct pm_proc *proc) +{ + ipi_mb_open(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id); +} + +/** + * pm_ipi_send_common() - Sends IPI request to the remote processor. + * @proc: Pointer to the processor who is initiating request. + * @payload: API id and call arguments to be written in IPI buffer. + * @is_blocking: if to trigger the notification in blocking mode or not. + * + * Send an IPI request to the power controller. Caller needs to hold + * the 'pm_secure_lock' lock. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ipi_send_common(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT], + uint32_t is_blocking) +{ + uint32_t offset = PM_OFFSET; + uintptr_t buffer_base = proc->ipi->buffer_base + + IPI_BUFFER_TARGET_REMOTE_OFFSET + + IPI_BUFFER_REQ_OFFSET; +#if IPI_CRC_CHECK + payload[PAYLOAD_CRC_POS] = calculate_crc(payload, IPI_W0_TO_W6_SIZE); +#endif + + /* Write payload into IPI buffer */ + for (size_t i = 0; i < PAYLOAD_ARG_CNT; i++) { + mmio_write_32(buffer_base + offset, payload[i]); + offset += PAYLOAD_ARG_SIZE; + } + + /* Generate IPI to remote processor */ + ipi_mb_notify(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id, + is_blocking); + + return PM_RET_SUCCESS; +} + +/** + * pm_ipi_send_non_blocking() - Sends IPI request to the remote processor + * without blocking notification. + * @proc: Pointer to the processor who is initiating request. + * @payload: API id and call arguments to be written in IPI buffer. + * + * Send an IPI request to the power controller. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_ipi_send_non_blocking(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT]) +{ + enum pm_ret_status ret; + + pm_ipi_lock_get(); + + ret = pm_ipi_send_common(proc, payload, IPI_NON_BLOCKING); + + pm_ipi_lock_release(); + + return ret; +} + +/** + * pm_ipi_send() - Sends IPI request to the remote processor. + * @proc: Pointer to the processor who is initiating request. + * @payload: API id and call arguments to be written in IPI buffer. + * + * Send an IPI request to the power controller. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_ipi_send(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT]) +{ + enum pm_ret_status ret; + + pm_ipi_lock_get(); + + ret = pm_ipi_send_common(proc, payload, IPI_BLOCKING); + + pm_ipi_lock_release(); + + return ret; +} + + +/** + * pm_ipi_buff_read() - Reads IPI response after remote processor has handled + * interrupt. + * @proc: Pointer to the processor who is waiting and reading response. + * @value: Used to return value from IPI buffer element (optional). + * @count: Number of values to return in @value. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, + uint32_t *value, size_t count) +{ + size_t i; + enum pm_ret_status ret; +#if IPI_CRC_CHECK + uint32_t *payload_ptr = value; + size_t j; + uint32_t response_payload[PAYLOAD_ARG_CNT]; +#endif + uintptr_t buffer_base = proc->ipi->buffer_base + + IPI_BUFFER_TARGET_REMOTE_OFFSET + + IPI_BUFFER_RESP_OFFSET; + + /* + * Read response from IPI buffer + * buf-0: success or error+reason + * buf-1: value + * buf-2: unused + * buf-3: unused + */ + for (i = 1; i <= count; i++) { + *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); + value++; + } + + ret = mmio_read_32(buffer_base); +#if IPI_CRC_CHECK + for (j = 0; j < PAYLOAD_ARG_CNT; j++) { + response_payload[j] = mmio_read_32(buffer_base + + (j * PAYLOAD_ARG_SIZE)); + } + + if (response_payload[PAYLOAD_CRC_POS] != + calculate_crc(response_payload, IPI_W0_TO_W6_SIZE)) { + NOTICE("ERROR in CRC response payload value:0x%x\n", + response_payload[PAYLOAD_CRC_POS]); + ret = PM_RET_ERROR_INVALID_CRC; + /* Payload data is invalid as CRC validation failed + * Clear the payload to avoid leakage of data to upper layers + */ + memset(payload_ptr, 0, count); + } +#endif + + return ret; +} + +/** + * pm_ipi_buff_read_callb() - Callback function that reads value from + * ipi response buffer. + * @value: Used to return value from IPI buffer element. + * @count: Number of values to return in @value. + * + * This callback function fills requested data in @value from ipi response + * buffer. + * + * Return: Returns status, either success or error. + * + */ +enum pm_ret_status pm_ipi_buff_read_callb(uint32_t *value, size_t count) +{ + size_t i; +#if IPI_CRC_CHECK + uint32_t *payload_ptr = value; + size_t j; + unsigned int response_payload[PAYLOAD_ARG_CNT] = {0}; +#endif + uintptr_t buffer_base = IPI_BUFFER_REMOTE_BASE + + IPI_BUFFER_TARGET_LOCAL_OFFSET + + IPI_BUFFER_REQ_OFFSET; + enum pm_ret_status ret = PM_RET_SUCCESS; + + if (count > IPI_BUFFER_MAX_WORDS) { + count = IPI_BUFFER_MAX_WORDS; + } + + for (i = 0; i <= count; i++) { + *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); + value++; + } +#if IPI_CRC_CHECK + for (j = 0; j < PAYLOAD_ARG_CNT; j++) { + response_payload[j] = mmio_read_32(buffer_base + + (j * PAYLOAD_ARG_SIZE)); + } + + if (response_payload[PAYLOAD_CRC_POS] != + calculate_crc(response_payload, IPI_W0_TO_W6_SIZE)) { + NOTICE("ERROR in CRC response payload value:0x%x\n", + response_payload[PAYLOAD_CRC_POS]); + ret = PM_RET_ERROR_INVALID_CRC; + /* Payload data is invalid as CRC validation failed + * Clear the payload to avoid leakage of data to upper layers + */ + memset(payload_ptr, 0, count); + } +#endif + return ret; +} + +/** + * pm_ipi_send_sync() - Sends IPI request to the remote processor. + * @proc: Pointer to the processor who is initiating request. + * @payload: API id and call arguments to be written in IPI buffer. + * @value: Used to return value from IPI buffer element (optional). + * @count: Number of values to return in @value. + * + * Send an IPI request to the power controller and wait for it to be handled. + * + * Return: Returns status, either success or error+reason and, optionally, + * @value. + * + */ +enum pm_ret_status pm_ipi_send_sync(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT], + uint32_t *value, size_t count) +{ + enum pm_ret_status ret; + + pm_ipi_lock_get(); + + ret = pm_ipi_send_common(proc, payload, IPI_BLOCKING); + if (ret != PM_RET_SUCCESS) { + goto unlock; + } + + ret = ERROR_CODE_MASK & (pm_ipi_buff_read(proc, value, count)); + +unlock: + pm_ipi_lock_release(); + + return ret; +} + +void pm_ipi_irq_enable(const struct pm_proc *proc) +{ + ipi_mb_enable_irq(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id); +} + +void pm_ipi_irq_clear(const struct pm_proc *proc) +{ + ipi_mb_ack(proc->ipi->local_ipi_id, proc->ipi->remote_ipi_id); +} + +uint32_t pm_ipi_irq_status(const struct pm_proc *proc) +{ + int32_t ret; + + ret = ipi_mb_enquire_status(proc->ipi->local_ipi_id, + proc->ipi->remote_ipi_id); + if (ret & IPI_MB_STATUS_RECV_PENDING) { + return 1; + } else { + return 0; + } +} + +#if IPI_CRC_CHECK +uint32_t calculate_crc(uint32_t payload[PAYLOAD_ARG_CNT], uint32_t bufsize) +{ + uint32_t crcinit = CRC_INIT_VALUE; + uint32_t order = CRC_ORDER; + uint32_t polynom = CRC_POLYNOM; + uint32_t i, j, c, bit, datain, crcmask, crchighbit; + uint32_t crc = crcinit; + + crcmask = ((uint32_t)((1U << (order - 1U)) - 1U) << 1U) | 1U; + crchighbit = (uint32_t)(1U << (order - 1U)); + + for (i = 0U; i < bufsize; i++) { + datain = mmio_read_8((unsigned long)payload + i); + c = datain; + j = 0x80U; + while (j != 0U) { + bit = crc & crchighbit; + crc <<= 1U; + if (0U != (c & j)) + bit ^= crchighbit; + if (bit != 0U) + crc ^= polynom; + j >>= 1U; + } + crc &= crcmask; + } + return crc; +} +#endif diff --git a/plat/xilinx/common/pm_service/pm_svc_main.c b/plat/xilinx/common/pm_service/pm_svc_main.c new file mode 100644 index 0000000..1e5808c --- /dev/null +++ b/plat/xilinx/common/pm_service/pm_svc_main.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2019-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Top-level SMC handler for Versal power management calls and + * IPI setup functions for communication with PMC. + */ + +#include <errno.h> +#include <stdbool.h> + +#include "../drivers/arm/gic/v3/gicv3_private.h" + +#include <common/runtime_svc.h> +#include <drivers/arm/gicv3.h> +#include <plat/common/platform.h> + +#include <plat_private.h> +#include "pm_api_sys.h" +#include "pm_client.h" +#include "pm_ipi.h" +#include "pm_svc_main.h" + +#define MODE 0x80000000U + +#define XSCUGIC_SGIR_EL1_INITID_SHIFT 24U +#define INVALID_SGI 0xFFU +#define PM_INIT_SUSPEND_CB (30U) +#define PM_NOTIFY_CB (32U) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_asgi1r_el1, S3_0_C12_C11_6) + +/* pm_up = true - UP, pm_up = false - DOWN */ +static bool pm_up; +static uint32_t sgi = (uint32_t)INVALID_SGI; + +static void notify_os(void) +{ + int32_t cpu; + uint32_t reg; + + cpu = plat_my_core_pos() + 1U; + + reg = (cpu | (sgi << XSCUGIC_SGIR_EL1_INITID_SHIFT)); + write_icc_asgi1r_el1(reg); +} + +static uint64_t ipi_fiq_handler(uint32_t id, uint32_t flags, void *handle, + void *cookie) +{ + uint32_t payload[4] = {0}; + enum pm_ret_status ret; + + VERBOSE("Received IPI FIQ from firmware\n"); + + (void)plat_ic_acknowledge_interrupt(); + + ret = pm_get_callbackdata(payload, ARRAY_SIZE(payload), 0, 0); + if (ret != PM_RET_SUCCESS) { + payload[0] = ret; + } + + switch (payload[0]) { + case PM_INIT_SUSPEND_CB: + case PM_NOTIFY_CB: + if (sgi != INVALID_SGI) { + notify_os(); + } + break; + case PM_RET_ERROR_INVALID_CRC: + pm_ipi_irq_clear(primary_proc); + WARN("Invalid CRC in the payload\n"); + break; + + default: + pm_ipi_irq_clear(primary_proc); + WARN("Invalid IPI payload\n"); + break; + } + + /* Clear FIQ */ + plat_ic_end_of_interrupt(id); + + return 0; +} + +/** + * pm_register_sgi() - PM register the IPI interrupt. + * @sgi_num: SGI number to be used for communication. + * @reset: Reset to invalid SGI when reset=1. + * + * Return: On success, the initialization function must return 0. + * Any other return value will cause the framework to ignore + * the service. + * + * Update the SGI number to be used. + * + */ +int32_t pm_register_sgi(uint32_t sgi_num, uint32_t reset) +{ + if (reset == 1U) { + sgi = INVALID_SGI; + return 0; + } + + if (sgi != INVALID_SGI) { + return -EBUSY; + } + + if (sgi_num >= GICV3_MAX_SGI_TARGETS) { + return -EINVAL; + } + + sgi = (uint32_t)sgi_num; + return 0; +} + +/** + * pm_setup() - PM service setup. + * + * Return: On success, the initialization function must return 0. + * Any other return value will cause the framework to ignore + * the service. + * + * Initialization functions for Versal power management for + * communicaton with PMC. + * + * Called from sip_svc_setup initialization function with the + * rt_svc_init signature. + * + */ +int32_t pm_setup(void) +{ + int32_t ret = 0; + + pm_ipi_init(primary_proc); + pm_up = true; + + /* + * Enable IPI IRQ + * assume the rich OS is OK to handle callback IRQs now. + * Even if we were wrong, it would not enable the IRQ in + * the GIC. + */ + pm_ipi_irq_enable(primary_proc); + + ret = request_intr_type_el3(PLAT_VERSAL_IPI_IRQ, ipi_fiq_handler); + if (ret != 0) { + WARN("BL31: registering IPI interrupt failed\n"); + } + + gicd_write_irouter(gicv3_driver_data->gicd_base, PLAT_VERSAL_IPI_IRQ, MODE); + return ret; +} + +/** + * eemi_for_compatibility() - EEMI calls handler for deprecated calls. + * @api_id: identifier for the API being called. + * @pm_arg: pointer to the argument data for the API call. + * @handle: Pointer to caller's context structure. + * @security_flag: SECURE_FLAG or NON_SECURE_FLAG. + * + * Return: If EEMI API found then, uintptr_t type address, else 0. + * + * Some EEMI API's use case needs to be changed in Linux driver, so they + * can take advantage of common EEMI handler in TF-A. As of now the old + * implementation of these APIs are required to maintain backward compatibility + * until their use case in linux driver changes. + * + */ +static uintptr_t eemi_for_compatibility(uint32_t api_id, uint32_t *pm_arg, + void *handle, uint32_t security_flag) +{ + enum pm_ret_status ret; + + switch (api_id) { + + case (uint32_t)PM_IOCTL: + { + uint32_t value = 0U; + + ret = pm_api_ioctl(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], pm_arg[4], + &value, security_flag); + if (ret == PM_RET_ERROR_NOTSUPPORTED) + return (uintptr_t)0; + + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + } + + case (uint32_t)PM_QUERY_DATA: + { + uint32_t data[PAYLOAD_ARG_CNT] = { 0 }; + + ret = pm_query_data(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], data, security_flag); + + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)data[0] << 32U), + (uint64_t)data[1] | ((uint64_t)data[2] << 32U)); + } + + case (uint32_t)PM_FEATURE_CHECK: + { + uint32_t result[PAYLOAD_ARG_CNT] = {0U}; + + ret = pm_feature_check(pm_arg[0], result, security_flag); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U), + (uint64_t)result[1] | ((uint64_t)result[2] << 32U)); + } + + case PM_LOAD_PDI: + { + ret = pm_load_pdi(pm_arg[0], pm_arg[1], pm_arg[2], + security_flag); + SMC_RET1(handle, (uint64_t)ret); + } + + default: + return (uintptr_t)0; + } +} + +/** + * eemi_psci_debugfs_handler() - EEMI API invoked from PSCI. + * @api_id: identifier for the API being called. + * @pm_arg: pointer to the argument data for the API call. + * @handle: Pointer to caller's context structure. + * @security_flag: SECURE_FLAG or NON_SECURE_FLAG. + * + * These EEMI APIs performs CPU specific power management tasks. + * These EEMI APIs are invoked either from PSCI or from debugfs in kernel. + * These calls require CPU specific processing before sending IPI request to + * Platform Management Controller. For example enable/disable CPU specific + * interrupts. This requires separate handler for these calls and may not be + * handled using common eemi handler. + * + * Return: If EEMI API found then, uintptr_t type address, else 0. + * + */ +static uintptr_t eemi_psci_debugfs_handler(uint32_t api_id, uint32_t *pm_arg, + void *handle, uint32_t security_flag) +{ + enum pm_ret_status ret; + + switch (api_id) { + + case (uint32_t)PM_SELF_SUSPEND: + ret = pm_self_suspend(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], security_flag); + SMC_RET1(handle, (u_register_t)ret); + + case (uint32_t)PM_FORCE_POWERDOWN: + ret = pm_force_powerdown(pm_arg[0], pm_arg[1], security_flag); + SMC_RET1(handle, (u_register_t)ret); + + case (uint32_t)PM_REQ_SUSPEND: + ret = pm_req_suspend(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], security_flag); + SMC_RET1(handle, (u_register_t)ret); + + case (uint32_t)PM_ABORT_SUSPEND: + ret = pm_abort_suspend(pm_arg[0], security_flag); + SMC_RET1(handle, (u_register_t)ret); + + case (uint32_t)PM_SYSTEM_SHUTDOWN: + ret = pm_system_shutdown(pm_arg[0], pm_arg[1], security_flag); + SMC_RET1(handle, (u_register_t)ret); + + default: + return (uintptr_t)0; + } +} + +/** + * TF_A_specific_handler() - SMC handler for TF-A specific functionality. + * @api_id: identifier for the API being called. + * @pm_arg: pointer to the argument data for the API call. + * @handle: Pointer to caller's context structure. + * @security_flag: SECURE_FLAG or NON_SECURE_FLAG. + * + * These EEMI calls performs functionality that does not require + * IPI transaction. The handler ends in TF-A and returns requested data to + * kernel from TF-A. + * + * Return: If TF-A specific API found then, uintptr_t type address, else 0 + * + */ +static uintptr_t TF_A_specific_handler(uint32_t api_id, uint32_t *pm_arg, + void *handle, uint32_t security_flag) +{ + switch (api_id) { + + case TF_A_PM_REGISTER_SGI: + { + int32_t ret; + + ret = pm_register_sgi(pm_arg[0], pm_arg[1]); + if (ret != 0) { + SMC_RET1(handle, (uint32_t)PM_RET_ERROR_ARGS); + } + + SMC_RET1(handle, (uint32_t)PM_RET_SUCCESS); + } + + case PM_GET_CALLBACK_DATA: + { + uint32_t result[4] = {0}; + enum pm_ret_status ret; + + ret = pm_get_callbackdata(result, ARRAY_SIZE(result), security_flag, 1U); + if (ret != 0) { + result[0] = ret; + } + + SMC_RET2(handle, + (uint64_t)result[0] | ((uint64_t)result[1] << 32U), + (uint64_t)result[2] | ((uint64_t)result[3] << 32U)); + } + + case PM_GET_TRUSTZONE_VERSION: + SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS | + ((uint64_t)TZ_VERSION << 32U)); + + default: + return (uintptr_t)0; + } +} + +/** + * eemi_handler() - Prepare EEMI payload and perform IPI transaction. + * @api_id: identifier for the API being called. + * @pm_arg: pointer to the argument data for the API call. + * @handle: Pointer to caller's context structure. + * @security_flag: SECURE_FLAG or NON_SECURE_FLAG. + * + * EEMI - Embedded Energy Management Interface is Xilinx proprietary protocol + * to allow communication between power management controller and different + * processing clusters. + * + * This handler prepares EEMI protocol payload received from kernel and performs + * IPI transaction. + * + * Return: If EEMI API found then, uintptr_t type address, else 0 + * + */ +static uintptr_t eemi_handler(uint32_t api_id, uint32_t *pm_arg, + void *handle, uint32_t security_flag) +{ + enum pm_ret_status ret; + uint32_t buf[PAYLOAD_ARG_CNT] = {0}; + + ret = pm_handle_eemi_call(security_flag, api_id, pm_arg[0], pm_arg[1], + pm_arg[2], pm_arg[3], pm_arg[4], + (uint64_t *)buf); + /* + * Two IOCTLs, to get clock name and pinctrl name of pm_query_data API + * receives 5 words of respoonse from firmware. Currently linux driver can + * receive only 4 words from TF-A. So, this needs to be handled separately + * than other eemi calls. + */ + if (api_id == (uint32_t)PM_QUERY_DATA) { + if ((pm_arg[0] == XPM_QID_CLOCK_GET_NAME || + pm_arg[0] == XPM_QID_PINCTRL_GET_FUNCTION_NAME) && + ret == PM_RET_SUCCESS) { + SMC_RET2(handle, (uint64_t)buf[0] | ((uint64_t)buf[1] << 32U), + (uint64_t)buf[2] | ((uint64_t)buf[3] << 32U)); + } + } + + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)buf[0] << 32U), + (uint64_t)buf[1] | ((uint64_t)buf[2] << 32U)); +} + +/** + * pm_smc_handler() - SMC handler for PM-API calls coming from EL1/EL2. + * @smc_fid: Function Identifier. + * @x1: SMC64 Arguments from kernel. + * @x2: SMC64 Arguments from kernel. + * @x3: SMC64 Arguments from kernel (upper 32-bits). + * @x4: Unused. + * @cookie: Unused. + * @handle: Pointer to caller's context structure. + * @flags: SECURE_FLAG or NON_SECURE_FLAG. + * + * Return: Unused. + * + * Determines that smc_fid is valid and supported PM SMC Function ID from the + * list of pm_api_ids, otherwise completes the request with + * the unknown SMC Function ID. + * + * The SMC calls for PM service are forwarded from SIP Service SMC handler + * function with rt_svc_handle signature. + * + */ +uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, const void *cookie, void *handle, uint64_t flags) +{ + uintptr_t ret; + uint32_t pm_arg[PAYLOAD_ARG_CNT] = {0}; + uint32_t security_flag = NON_SECURE_FLAG; + uint32_t api_id; + bool status = false, status_tmp = false; + + /* Handle case where PM wasn't initialized properly */ + if (pm_up == false) { + SMC_RET1(handle, SMC_UNK); + } + + /* + * Mark BIT24 payload (i.e 1st bit of pm_arg[3] ) as secure (0) + * if smc called is secure + * + * Add redundant macro call to immune the code from glitches + */ + SECURE_REDUNDANT_CALL(status, status_tmp, is_caller_secure, flags); + if ((status != false) && (status_tmp != false)) { + security_flag = SECURE_FLAG; + } + + pm_arg[0] = (uint32_t)x1; + pm_arg[1] = (uint32_t)(x1 >> 32U); + pm_arg[2] = (uint32_t)x2; + pm_arg[3] = (uint32_t)(x2 >> 32U); + pm_arg[4] = (uint32_t)x3; + (void)(x4); + api_id = smc_fid & FUNCID_NUM_MASK; + + ret = eemi_for_compatibility(api_id, pm_arg, handle, security_flag); + if (ret != (uintptr_t)0) { + return ret; + } + + ret = eemi_psci_debugfs_handler(api_id, pm_arg, handle, flags); + if (ret != (uintptr_t)0) { + return ret; + } + + ret = TF_A_specific_handler(api_id, pm_arg, handle, security_flag); + if (ret != (uintptr_t)0) { + return ret; + } + + ret = eemi_handler(api_id, pm_arg, handle, security_flag); + + return ret; +} diff --git a/plat/xilinx/common/tsp/tsp.mk b/plat/xilinx/common/tsp/tsp.mk new file mode 100644 index 0000000..b80f531 --- /dev/null +++ b/plat/xilinx/common/tsp/tsp.mk @@ -0,0 +1,8 @@ +# +# Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# TSP source files for AMD-Xilinx platforms +BL32_SOURCES += plat/common/aarch64/platform_mp_stack.S \ + plat/xilinx/common/tsp/tsp_plat_setup.c diff --git a/plat/xilinx/common/tsp/tsp_plat_setup.c b/plat/xilinx/common/tsp/tsp_plat_setup.c new file mode 100644 index 0000000..21c29c3 --- /dev/null +++ b/plat/xilinx/common/tsp/tsp_plat_setup.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023, Advanced Micro Devices. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/bl_common.h> +#include <common/debug.h> +#include <drivers/arm/pl011.h> +#include <drivers/console.h> +#include <plat/arm/common/plat_arm.h> +#include <platform_tsp.h> + +#include <plat_private.h> + +/******************************************************************************* + * Initialize the UART + ******************************************************************************/ +void tsp_early_platform_setup(void) +{ + /* + * Register a different console than already in use to display + * messages from TSP + */ + static console_t tsp_boot_console; + int32_t rc; + +#if defined(PLAT_zynqmp) + rc = console_cdns_register((uintptr_t)UART_BASE, + (uint32_t)get_uart_clk(), + (uint32_t)UART_BAUDRATE, + &tsp_boot_console); +#else + rc = console_pl011_register((uintptr_t)UART_BASE, + (uint32_t)get_uart_clk(), + (uint32_t)UART_BAUDRATE, + &tsp_boot_console); +#endif + + if (rc == 0) { + panic(); + } + + console_set_scope(&tsp_boot_console, + CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_BOOT); +} + +/******************************************************************************* + * Perform platform specific setup placeholder + ******************************************************************************/ +void tsp_platform_setup(void) +{ +/* + * For ZynqMP, the GICv2 driver needs to be initialized in S-EL1, + * and for other platforms, the GICv3 driver is initialized in EL3. + * This is because S-EL1 can use GIC system registers to manage + * interrupts and does not need to be initialized again in SEL1. + */ +#if defined(PLAT_zynqmp) + plat_arm_gic_driver_init(); + plat_arm_gic_init(); +#endif +} + +/******************************************************************************* + * Perform the very early platform specific architectural setup here. At the + * moment this is only initializes the MMU + ******************************************************************************/ +void tsp_plat_arch_setup(void) +{ + const mmap_region_t bl_regions[] = { + MAP_REGION_FLAT(BL32_BASE, BL32_END - BL32_BASE, + MT_MEMORY | MT_RW | MT_SECURE), + MAP_REGION_FLAT(BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, + MT_CODE | MT_SECURE), + MAP_REGION_FLAT(BL_RO_DATA_BASE, BL_RO_DATA_END - BL_RO_DATA_BASE, + MT_RO_DATA | MT_SECURE), +#if defined(PLAT_zynqmp) || defined(PLAT_versal) + MAP_REGION_FLAT(BL_COHERENT_RAM_BASE, + BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE, + MT_DEVICE | MT_RW | MT_SECURE), +#endif + {0} + }; + + setup_page_tables(bl_regions, plat_get_mmap()); + enable_mmu_el1(0); +} diff --git a/plat/xilinx/common/versal.c b/plat/xilinx/common/versal.c new file mode 100644 index 0000000..3ea022c --- /dev/null +++ b/plat/xilinx/common/versal.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/smccc.h> +#include <services/arm_arch_svc.h> + +#include <plat_private.h> +#include <plat_startup.h> +#include <pm_api_sys.h> + +/** + * plat_is_smccc_feature_available() - This function checks whether SMCCC + * feature is availabile for platform. + * @fid: SMCCC function id. + * + * Return: SMC_ARCH_CALL_SUCCESS - if SMCCC feature is available. + * SMC_ARCH_CALL_NOT_SUPPORTED - Otherwise. + * + */ +int32_t plat_is_smccc_feature_available(u_register_t fid) +{ + switch (fid) { + case SMCCC_ARCH_SOC_ID: + return SMC_ARCH_CALL_SUCCESS; + default: + return SMC_ARCH_CALL_NOT_SUPPORTED; + } +} + +/** + * plat_get_soc_version() - Get the SOC version of the platform. + * + * Return: SiP defined SoC version in JEP-106. + * + * This function is called when the SoC_ID_type == 0. + * For further details please refer to section 7.4 of SMC Calling Convention. + */ +int32_t plat_get_soc_version(void) +{ + uint32_t manfid; + + manfid = SOC_ID_SET_JEP_106(JEDEC_XILINX_BKID, JEDEC_XILINX_MFID); + + return (int32_t)(manfid | (platform_version & SOC_ID_IMPL_DEF_MASK)); +} + +/** + * plat_get_soc_revision() - Get the SOC revision for the platform. + * + * Return: SiP defined SoC revision. + * + * This function is called when the SoC_ID_type == 1 + * For further details please refer to section 7.4 of SMC Calling Convention + */ +int32_t plat_get_soc_revision(void) +{ + return (platform_id & SOC_ID_REV_MASK); +} |