diff options
Diffstat (limited to 'plat/xilinx')
93 files changed, 20695 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); +} diff --git a/plat/xilinx/versal/aarch64/versal_common.c b/plat/xilinx/versal/aarch64/versal_common.c new file mode 100644 index 0000000..aba190d --- /dev/null +++ b/plat/xilinx/versal/aarch64/versal_common.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <drivers/generic_delay_timer.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> + +#include <plat_common.h> +#include <plat_ipi.h> +#include <plat_private.h> +#include <pm_api_sys.h> +#include <versal_def.h> + +uint32_t platform_id, platform_version; + +/* + * Table of regions to map using the MMU. + * This doesn't include TZRAM as the 'mem_layout' argument passed to + * configure_mmu_elx() will give the available subset of that, + */ +const mmap_region_t plat_versal_mmap[] = { + MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(CRF_BASE, CRF_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(PLAT_ARM_CCI_BASE, PLAT_ARM_CCI_SIZE, MT_DEVICE | MT_RW | + MT_SECURE), + { 0 } +}; + +const mmap_region_t *plat_get_mmap(void) +{ + return plat_versal_mmap; +} + +static void versal_print_platform_name(void) +{ + NOTICE("TF-A running on %s\n", PLATFORM_NAME); +} + +void versal_config_setup(void) +{ + /* Configure IPI data for versal */ + versal_ipi_config_table_init(); + + versal_print_platform_name(); + + generic_delay_timer_init(); +} + +uint32_t plat_get_syscnt_freq2(void) +{ + return VERSAL_CPU_CLOCK; +} + +void board_detection(void) +{ + uint32_t plat_info[2]; + + if (pm_get_chipid(plat_info) != PM_RET_SUCCESS) { + /* If the call is failed we cannot proceed with further + * setup. TF-A to panic in this situation. + */ + NOTICE("Failed to read the chip information"); + panic(); + } + + platform_id = FIELD_GET(PLATFORM_MASK, plat_info[1]); + platform_version = FIELD_GET(PLATFORM_VERSION_MASK, plat_info[1]); +} + +uint32_t get_uart_clk(void) +{ + return UART_CLOCK; +} diff --git a/plat/xilinx/versal/aarch64/versal_helpers.S b/plat/xilinx/versal/aarch64/versal_helpers.S new file mode 100644 index 0000000..350ddc4 --- /dev/null +++ b/plat/xilinx/versal/aarch64/versal_helpers.S @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include <arch.h> +#include <asm_macros.S> +#include <drivers/arm/gicv3.h> + +#include <platform_def.h> + + .globl plat_secondary_cold_boot_setup + .globl plat_is_my_cpu_primary + .globl versal_calc_core_pos + .globl platform_mem_init + .globl plat_my_core_pos + + /* ----------------------------------------------------- + * void plat_secondary_cold_boot_setup (void); + * + * This function performs any platform specific actions + * needed for a secondary cpu after a cold reset e.g + * mark the cpu's presence, mechanism to place it in a + * holding pen etc. + * TODO: Should we read the PSYS register to make sure + * that the request has gone through. + * ----------------------------------------------------- + */ +func plat_secondary_cold_boot_setup + mrs x0, mpidr_el1 + + /* + * There is no sane reason to come out of this wfi. This + * cpu will be powered on and reset by the cpu_on pm api + */ + dsb sy + bl plat_panic_handler +endfunc plat_secondary_cold_boot_setup + +func plat_is_my_cpu_primary + mov x9, x30 + bl plat_my_core_pos + cmp x0, #VERSAL_PRIMARY_CPU + cset x0, eq + ret x9 +endfunc plat_is_my_cpu_primary + + /* ----------------------------------------------------- + * unsigned int plat_my_core_pos(void) + * This function uses the versal_calc_core_pos() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_my_core_pos + mrs x0, mpidr_el1 + b versal_calc_core_pos +endfunc plat_my_core_pos + +func versal_calc_core_pos + and x1, x0, #MPIDR_CPU_MASK + and x0, x0, #MPIDR_CLUSTER_MASK + add x0, x1, x0, LSR #6 + ret +endfunc versal_calc_core_pos + + /* --------------------------------------------------------------------- + * We don't need to carry out any memory initialization on VERSAL + * platform. The Secure RAM is accessible straight away. + * --------------------------------------------------------------------- + */ +func platform_mem_init + ret +endfunc platform_mem_init diff --git a/plat/xilinx/versal/bl31_versal_setup.c b/plat/xilinx/versal/bl31_versal_setup.c new file mode 100644 index 0000000..cd105c6 --- /dev/null +++ b/plat/xilinx/versal/bl31_versal_setup.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <bl31/bl31.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> +#include <plat_arm.h> +#include <plat_console.h> + +#include <plat_fdt.h> +#include <plat_private.h> +#include <plat_startup.h> +#include "pm_api_sys.h" +#include "pm_client.h" +#include <pm_ipi.h> +#include <versal_def.h> + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +/* + * Return a pointer to the 'entry_point_info' structure of the next image for + * the security state specified. BL33 corresponds to the non-secure image type + * while BL32 corresponds to the secure image type. A NULL pointer is returned + * if the image does not exist. + */ +entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) +{ + assert(sec_state_is_valid(type)); + + if (type == NON_SECURE) { + return &bl33_image_ep_info; + } + + return &bl32_image_ep_info; +} + +/* + * Set the build time defaults,if we can't find any config data. + */ +static inline void bl31_set_default_config(void) +{ + bl32_image_ep_info.pc = (uintptr_t)BL32_BASE; + bl32_image_ep_info.spsr = (uint32_t)arm_get_spsr_for_bl32_entry(); + bl33_image_ep_info.pc = (uintptr_t)plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = (uint32_t)SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); +} + +/* + * Perform any BL31 specific platform actions. Here is an opportunity to copy + * parameters passed by the calling EL (S-EL1 in BL2 & S-EL3 in BL1) before they + * are lost (potentially). This needs to be done before the MMU is initialized + * so that the memory layout can be used while creating page tables. + */ +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + uint64_t tfa_handoff_addr; + uint32_t payload[PAYLOAD_ARG_CNT], max_size = HANDOFF_PARAMS_MAX_SIZE; + enum pm_ret_status ret_status; + uint64_t addr[HANDOFF_PARAMS_MAX_SIZE]; + + setup_console(); + + /* Initialize the platform config for future decision making */ + versal_config_setup(); + + /* Get platform related information */ + board_detection(); + + /* + * Do initial security configuration to allow DRAM/device access. On + * Base VERSAL only DRAM security is programmable (via TrustZone), but + * other platforms might have more programmable security devices + * present. + */ + + /* Populate common information for BL32 and BL33 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + + PM_PACK_PAYLOAD4(payload, LOADER_MODULE_ID, 1, PM_LOAD_GET_HANDOFF_PARAMS, + (uintptr_t)addr >> 32U, (uintptr_t)addr, max_size); + ret_status = pm_ipi_send_sync(primary_proc, payload, NULL, 0); + if (ret_status == PM_RET_SUCCESS) { + INFO("BL31: GET_HANDOFF_PARAMS call success=%d\n", ret_status); + tfa_handoff_addr = (uintptr_t)&addr; + } else { + ERROR("BL31: GET_HANDOFF_PARAMS Failed, read tfa_handoff_addr from reg\n"); + tfa_handoff_addr = mmio_read_32(PMC_GLOBAL_GLOB_GEN_STORAGE4); + } + + enum xbl_handoff ret = xbl_handover(&bl32_image_ep_info, + &bl33_image_ep_info, + tfa_handoff_addr); + if (ret == XBL_HANDOFF_NO_STRUCT || ret == XBL_HANDOFF_INVAL_STRUCT) { + bl31_set_default_config(); + } else if (ret == XBL_HANDOFF_TOO_MANY_PARTS) { + ERROR("BL31: Error too many partitions %u\n", ret); + } else if (ret != XBL_HANDOFF_SUCCESS) { + panic(); + } else { + INFO("BL31: PLM to TF-A handover success %u\n", ret); + + /* + * The BL32 load address is indicated as 0x0 in the handoff + * parameters, which is different from the default/user-provided + * load address of 0x60000000 but the flags are correctly + * configured. Consequently, in this scenario, set the PC + * to the requested BL32_BASE address. + */ + + /* TODO: Remove the following check once this is fixed from PLM */ + if (bl32_image_ep_info.pc == 0 && bl32_image_ep_info.spsr != 0) { + bl32_image_ep_info.pc = (uintptr_t)BL32_BASE; + } + } + + NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc); + NOTICE("BL31: Non secure code at 0x%lx\n", bl33_image_ep_info.pc); +} + +static versal_intr_info_type_el3_t type_el3_interrupt_table[MAX_INTR_EL3]; + +int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) +{ + static uint32_t index; + uint32_t i; + + /* Validate 'handler' and 'id' parameters */ + if (handler == NULL || index >= MAX_INTR_EL3) { + return -EINVAL; + } + + /* Check if a handler has already been registered */ + for (i = 0; i < index; i++) { + if (id == type_el3_interrupt_table[i].id) { + return -EALREADY; + } + } + + type_el3_interrupt_table[index].id = id; + type_el3_interrupt_table[index].handler = handler; + + index++; + + return 0; +} + +static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + uint32_t intr_id; + uint32_t i; + interrupt_type_handler_t handler = NULL; + + intr_id = plat_ic_get_pending_interrupt_id(); + + for (i = 0; i < MAX_INTR_EL3; i++) { + if (intr_id == type_el3_interrupt_table[i].id) { + handler = type_el3_interrupt_table[i].handler; + } + } + + if (handler != NULL) { + return handler(intr_id, flags, handle, cookie); + } + + return 0; +} + +void bl31_platform_setup(void) +{ + prepare_dtb(); + + /* Initialize the gic cpu and distributor interfaces */ + plat_versal_gic_driver_init(); + plat_versal_gic_init(); +} + +void bl31_plat_runtime_setup(void) +{ + uint64_t flags = 0; + int32_t rc; + + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_EL3, + rdo_el3_interrupt_handler, flags); + if (rc != 0) { + panic(); + } + + console_switch_state(CONSOLE_FLAG_RUNTIME); +} + +/* + * Perform the very early platform specific architectural setup here. + */ +void bl31_plat_arch_setup(void) +{ + plat_arm_interconnect_init(); + plat_arm_interconnect_enter_coherency(); + + const mmap_region_t bl_regions[] = { +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE) && \ + (!defined(PLAT_XLAT_TABLES_DYNAMIC))) + MAP_REGION_FLAT(XILINX_OF_BOARD_DTB_ADDR, XILINX_OF_BOARD_DTB_MAX_SIZE, + MT_MEMORY | MT_RW | MT_NS), +#endif + MAP_REGION_FLAT(BL31_BASE, BL31_END - BL31_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), + MAP_REGION_FLAT(BL_COHERENT_RAM_BASE, + BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE, + MT_DEVICE | MT_RW | MT_SECURE), + {0} + }; + + setup_page_tables(bl_regions, plat_get_mmap()); + enable_mmu(0); +} diff --git a/plat/xilinx/versal/include/plat_ipi.h b/plat/xilinx/versal/include/plat_ipi.h new file mode 100644 index 0000000..4f7cb5f --- /dev/null +++ b/plat/xilinx/versal/include/plat_ipi.h @@ -0,0 +1,70 @@ +/* + * 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 IPI management enums and defines */ + +#ifndef PLAT_IPI_H +#define PLAT_IPI_H + +#include <stdint.h> + +#include <ipi.h> + +/********************************************************************* + * IPI agent IDs macros + ********************************************************************/ +#define IPI_ID_PMC 1U +#define IPI_ID_APU 2U +#define IPI_ID_RPU0 3U +#define IPI_ID_RPU1 4U +#define IPI_ID_3 5U +#define IPI_ID_4 6U +#define IPI_ID_5 7U + +/********************************************************************* + * IPI message buffers + ********************************************************************/ +#define IPI_BUFFER_BASEADDR 0xFF3F0000U + +#define IPI_LOCAL_ID IPI_ID_APU +#define IPI_REMOTE_ID IPI_ID_PMC + +#define IPI_BUFFER_LOCAL_BASE (IPI_BUFFER_BASEADDR + (IPI_LOCAL_ID * 0x200U)) +#define IPI_BUFFER_REMOTE_BASE (IPI_BUFFER_BASEADDR + (IPI_REMOTE_ID * 0x200U)) + +#define IPI_BUFFER_TARGET_LOCAL_OFFSET (IPI_LOCAL_ID * 0x40U) +#define IPI_BUFFER_TARGET_REMOTE_OFFSET (IPI_REMOTE_ID * 0x40U) + +#define IPI_BUFFER_MAX_WORDS 8 + +#define IPI_BUFFER_REQ_OFFSET 0x0U +#define IPI_BUFFER_RESP_OFFSET 0x20U + +/********************************************************************* + * Platform specific IPI API declarations + ********************************************************************/ + +/* Configure IPI table for versal */ +void versal_ipi_config_table_init(void); + +/* IPI registers and bitfields */ +#define PMC_REG_BASE U(0xFF320000) +#define PMC_IPI_TRIG_BIT (1U << 1U) +#define IPI0_REG_BASE U(0xFF330000) +#define IPI0_TRIG_BIT (1U << 2U) +#define IPI1_REG_BASE U(0xFF340000) +#define IPI1_TRIG_BIT (1U << 3U) +#define IPI2_REG_BASE U(0xFF350000) +#define IPI2_TRIG_BIT (1U << 4U) +#define IPI3_REG_BASE U(0xFF360000) +#define IPI3_TRIG_BIT (1U << 5U) +#define IPI4_REG_BASE U(0xFF370000) +#define IPI4_TRIG_BIT (1U << 5U) +#define IPI5_REG_BASE U(0xFF380000) +#define IPI5_TRIG_BIT (1U << 6U) + +#endif /* PLAT_IPI_H */ diff --git a/plat/xilinx/versal/include/plat_macros.S b/plat/xilinx/versal/include/plat_macros.S new file mode 100644 index 0000000..41193a5 --- /dev/null +++ b/plat/xilinx/versal/include/plat_macros.S @@ -0,0 +1,111 @@ +/* + * Copyright (c) 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 + */ + +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv2.h> +#include <drivers/arm/gicv3.h> + +#include "../include/platform_def.h" + +.section .rodata.gic_reg_name, "aS" +/* Applicable only to GICv2 and GICv3 with SRE disabled (legacy mode) */ +gicc_regs: + .asciz "gicc_hppir", "gicc_ahppir", "gicc_ctlr", "" + +/* Applicable only to GICv3 with SRE enabled */ +icc_regs: + .asciz "icc_hppir0_el1", "icc_hppir1_el1", "icc_ctlr_el3", "" + +/* Registers common to both GICv2 and GICv3 */ +gicd_pend_reg: + .asciz "gicd_ispendr regs (Offsets 0x200 - 0x278)\n Offset:\t\t\tvalue\n" +newline: + .asciz "\n" +spacer: + .asciz ":\t\t0x" + + /* --------------------------------------------- + * The below utility macro prints out relevant GIC + * registers whenever an unhandled exception is + * taken in BL31 on Versal platform. + * Expects: GICD base in x16, GICC base in x17 + * Clobbers: x0 - x10, sp + * --------------------------------------------- + */ + .macro versal_print_gic_regs + /* Check for GICv3 system register access */ + mrs x7, id_aa64pfr0_el1 + ubfx x7, x7, #ID_AA64PFR0_GIC_SHIFT, #ID_AA64PFR0_GIC_WIDTH + cmp x7, #1 + b.ne print_gicv2 + + /* Check for SRE enable */ + mrs x8, ICC_SRE_EL3 + tst x8, #ICC_SRE_SRE_BIT + b.eq print_gicv2 + + /* Load the icc reg list to x6 */ + adr x6, icc_regs + /* Load the icc regs to gp regs used by str_in_crash_buf_print */ + mrs x8, ICC_HPPIR0_EL1 + mrs x9, ICC_HPPIR1_EL1 + mrs x10, ICC_CTLR_EL3 + /* Store to the crash buf and print to console */ + bl str_in_crash_buf_print + b print_gic_common + +print_gicv2: + /* Load the gicc reg list to x6 */ + adr x6, gicc_regs + /* Load the gicc regs to gp regs used by str_in_crash_buf_print */ + ldr w8, [x17, #GICC_HPPIR] + ldr w9, [x17, #GICC_AHPPIR] + ldr w10, [x17, #GICC_CTLR] + /* Store to the crash buf and print to console */ + bl str_in_crash_buf_print + +print_gic_common: + /* Print the GICD_ISPENDR regs */ + add x7, x16, #GICD_ISPENDR + adr x4, gicd_pend_reg + bl asm_print_str +gicd_ispendr_loop: + sub x4, x7, x16 + cmp x4, #0x280 + b.eq exit_print_gic_regs + bl asm_print_hex + + adr x4, spacer + bl asm_print_str + + ldr x4, [x7], #8 + bl asm_print_hex + + adr x4, newline + bl asm_print_str + b gicd_ispendr_loop +exit_print_gic_regs: + .endm + + /* --------------------------------------------- + * The below required platform porting macro + * prints out relevant GIC and CCI registers + * whenever an unhandled exception is taken in + * BL31. + * Clobbers: x0 - x10, x16, x17, sp + * --------------------------------------------- + */ + .macro plat_crash_print_regs + mov_imm x17, PLAT_GICD_BASE_VALUE + mov_imm x16, PLAT_GICR_BASE_VALUE + versal_print_gic_regs + .endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/xilinx/versal/include/plat_pm_common.h b/plat/xilinx/versal/include/plat_pm_common.h new file mode 100644 index 0000000..0b4a115 --- /dev/null +++ b/plat/xilinx/versal/include/plat_pm_common.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Contains platform specific definitions of commonly used macros data types + * for PU Power Management. This file should be common for all PU's. + */ + +#ifndef PLAT_PM_COMMON_H +#define PLAT_PM_COMMON_H + +#include <stdint.h> + +#include <common/debug.h> + +#include "pm_defs.h" + +#define NON_SECURE_FLAG 1U +#define SECURE_FLAG 0U + +#endif /* PLAT_PM_COMMON_H */ diff --git a/plat/xilinx/versal/include/plat_private.h b/plat/xilinx/versal/include/plat_private.h new file mode 100644 index 0000000..a4210cd --- /dev/null +++ b/plat/xilinx/versal/include/plat_private.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_PRIVATE_H +#define PLAT_PRIVATE_H + +#include <bl31/interrupt_mgmt.h> +#include <lib/xlat_tables/xlat_tables_v2.h> + +typedef struct versal_intr_info_type_el3 { + uint32_t id; + interrupt_type_handler_t handler; +} versal_intr_info_type_el3_t; + +uint32_t get_uart_clk(void); +void versal_config_setup(void); + +const mmap_region_t *plat_get_mmap(void); + +extern uint32_t platform_id, platform_version; + +void board_detection(void); +void plat_versal_gic_driver_init(void); +void plat_versal_gic_init(void); +void plat_versal_gic_cpuif_enable(void); +void plat_versal_gic_cpuif_disable(void); +void plat_versal_gic_pcpu_init(void); +void plat_versal_gic_save(void); +void plat_versal_gic_resume(void); + +uint32_t versal_calc_core_pos(u_register_t mpidr); +/* + * Register handler to specific GIC entrance + * for INTR_TYPE_EL3 type of interrupt + */ +int32_t request_intr_type_el3(uint32_t irq, interrupt_type_handler_t fiq_handler); + +#endif /* PLAT_PRIVATE_H */ diff --git a/plat/xilinx/versal/include/platform_def.h b/plat/xilinx/versal/include/platform_def.h new file mode 100644 index 0000000..286a706 --- /dev/null +++ b/plat/xilinx/versal/include/platform_def.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <arch.h> +#include "versal_def.h" + +/******************************************************************************* + * Generic platform constants + ******************************************************************************/ + +/* Size of cacheable stacks */ +#define PLATFORM_STACK_SIZE U(0x440) + +#define PLATFORM_CORE_COUNT U(2) +#define PLAT_MAX_PWR_LVL U(1) +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_OFF_STATE U(2) + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ +/* + * Put BL31 at the top of the Trusted SRAM (just below the shared memory, if + * present). BL31_BASE is calculated using the current BL31 debug size plus a + * little space for growth. + */ +#ifndef VERSAL_ATF_MEM_BASE +# define BL31_BASE U(0xfffe0000) +# define BL31_LIMIT U(0x100000000) +#else +# define BL31_BASE U(VERSAL_ATF_MEM_BASE) +# define BL31_LIMIT U(VERSAL_ATF_MEM_BASE + VERSAL_ATF_MEM_SIZE) +# ifdef VERSAL_ATF_MEM_PROGBITS_SIZE +# define BL31_PROGBITS_LIMIT U(VERSAL_ATF_MEM_BASE + VERSAL_ATF_MEM_PROGBITS_SIZE) +# endif +#endif + +/******************************************************************************* + * BL32 specific defines. + ******************************************************************************/ +#ifndef VERSAL_BL32_MEM_BASE +# define BL32_BASE U(0x60000000) +# define BL32_LIMIT U(0x80000000) +#else +# define BL32_BASE U(VERSAL_BL32_MEM_BASE) +# define BL32_LIMIT U(VERSAL_BL32_MEM_BASE + VERSAL_BL32_MEM_SIZE) +#endif + +/******************************************************************************* + * BL33 specific defines. + ******************************************************************************/ +#ifndef PRELOADED_BL33_BASE +# define PLAT_ARM_NS_IMAGE_BASE U(0x8000000) +#else +# define PLAT_ARM_NS_IMAGE_BASE U(PRELOADED_BL33_BASE) +#endif + +/******************************************************************************* + * TSP specific defines. + ******************************************************************************/ +#define TSP_SEC_MEM_BASE BL32_BASE +#define TSP_SEC_MEM_SIZE (BL32_LIMIT - BL32_BASE) + +/* ID of the secure physical generic timer interrupt used by the TSP */ +#define TSP_IRQ_SEC_PHY_TIMER ARM_IRQ_SEC_PHY_TIMER + +/******************************************************************************* + * Platform specific page table and MMU setup constants + ******************************************************************************/ +#define PLAT_PHY_ADDR_SPACE_SIZE (1ull << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << 32) + +#define XILINX_OF_BOARD_DTB_MAX_SIZE U(0x200000) + +#define PLAT_OCM_BASE U(0xFFFE0000) +#define PLAT_OCM_LIMIT U(0xFFFFFFFF) + +#define IS_TFA_IN_OCM(x) ((x >= PLAT_OCM_BASE) && (x < PLAT_OCM_LIMIT)) + +#ifndef MAX_MMAP_REGIONS +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) +#define MAX_MMAP_REGIONS 9 +#else +#define MAX_MMAP_REGIONS 8 +#endif +#endif + +#ifndef MAX_XLAT_TABLES +#if !IS_TFA_IN_OCM(BL31_BASE) +#define MAX_XLAT_TABLES 9 +#else +#define MAX_XLAT_TABLES 5 +#endif +#endif + +#define CACHE_WRITEBACK_SHIFT 6 +#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) + +#define PLAT_GICD_BASE_VALUE U(0xF9000000) +#define PLAT_GICR_BASE_VALUE U(0xF9080000) + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLAT_VERSAL_G1S_IRQS VERSAL_IRQ_SEC_PHY_TIMER +#define PLAT_VERSAL_G0_IRQS VERSAL_IRQ_SEC_PHY_TIMER +#define PLAT_VERSAL_IPI_IRQ U(62) + +#define PLAT_VERSAL_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(VERSAL_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL) + +#define PLAT_VERSAL_G0_IRQ_PROPS(grp) \ + INTR_PROP_DESC(PLAT_VERSAL_IPI_IRQ, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + +#define IRQ_MAX 142U + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/xilinx/versal/include/versal_def.h b/plat/xilinx/versal/include/versal_def.h new file mode 100644 index 0000000..92c0ba6 --- /dev/null +++ b/plat/xilinx/versal/include/versal_def.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018-2022, 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 + */ + +#ifndef VERSAL_DEF_H +#define VERSAL_DEF_H + +#include <plat/arm/common/smccc_def.h> +#include <plat/common/common_def.h> + +#define PLATFORM_MASK GENMASK(27U, 24U) +#define PLATFORM_VERSION_MASK GENMASK(31U, 28U) + +/* number of interrupt handlers. increase as required */ +#define MAX_INTR_EL3 2 +/* List all consoles */ +#define VERSAL_CONSOLE_ID_pl011 1 +#define VERSAL_CONSOLE_ID_pl011_0 1 +#define VERSAL_CONSOLE_ID_pl011_1 2 +#define VERSAL_CONSOLE_ID_dcc 3 + +#define CONSOLE_IS(con) (VERSAL_CONSOLE_ID_ ## con == VERSAL_CONSOLE) + +/* List all supported platforms */ +#define VERSAL_PLATFORM_ID_versal_virt 1 +#define VERSAL_PLATFORM_ID_spp_itr6 2 +#define VERSAL_PLATFORM_ID_emu_itr6 3 +#define VERSAL_PLATFORM_ID_silicon 4 + +#define VERSAL_PLATFORM_IS(con) (VERSAL_PLATFORM_ID_ ## con == VERSAL_PLATFORM) + +/* Firmware Image Package */ +#define VERSAL_PRIMARY_CPU 0 + +/******************************************************************************* + * memory map related constants + ******************************************************************************/ +#define DEVICE0_BASE 0xFF000000 +#define DEVICE0_SIZE 0x00E00000 +#define DEVICE1_BASE 0xF9000000 +#define DEVICE1_SIZE 0x00800000 + +/******************************************************************************* + * IRQ constants + ******************************************************************************/ +#define VERSAL_IRQ_SEC_PHY_TIMER U(29) +#define ARM_IRQ_SEC_PHY_TIMER 29 + +/******************************************************************************* + * CCI-400 related constants + ******************************************************************************/ +#define PLAT_ARM_CCI_BASE 0xFD000000 +#define PLAT_ARM_CCI_SIZE 0x00100000 +#define PLAT_ARM_CCI_CLUSTER0_SL_IFACE_IX 4 +#define PLAT_ARM_CCI_CLUSTER1_SL_IFACE_IX 5 + +/******************************************************************************* + * UART related constants + ******************************************************************************/ +#define VERSAL_UART0_BASE 0xFF000000 +#define VERSAL_UART1_BASE 0xFF010000 + +#if CONSOLE_IS(pl011) || CONSOLE_IS(dcc) +# define UART_BASE VERSAL_UART0_BASE +#elif CONSOLE_IS(pl011_1) +# define UART_BASE VERSAL_UART1_BASE +#else +# error "invalid VERSAL_CONSOLE" +#endif + +/******************************************************************************* + * Platform related constants + ******************************************************************************/ +#if VERSAL_PLATFORM_IS(versal_virt) +# define PLATFORM_NAME "Versal Virt" +# define UART_CLOCK 25000000 +# define UART_BAUDRATE 115200 +# define VERSAL_CPU_CLOCK 2720000 +#elif VERSAL_PLATFORM_IS(silicon) +# define PLATFORM_NAME "Versal Silicon" +# define UART_CLOCK 100000000 +# define UART_BAUDRATE 115200 +# define VERSAL_CPU_CLOCK 100000000 +#elif VERSAL_PLATFORM_IS(spp_itr6) +# define PLATFORM_NAME "SPP ITR6" +# define UART_CLOCK 25000000 +# define UART_BAUDRATE 115200 +# define VERSAL_CPU_CLOCK 2720000 +#elif VERSAL_PLATFORM_IS(emu_itr6) +# define PLATFORM_NAME "EMU ITR6" +# define UART_CLOCK 212000 +# define UART_BAUDRATE 9600 +# define VERSAL_CPU_CLOCK 212000 +#endif + +/* Access control register defines */ +#define ACTLR_EL3_L2ACTLR_BIT (1 << 6) +#define ACTLR_EL3_CPUACTLR_BIT (1 << 0) + +/* For cpu reset APU space here too 0xFE5F1000 CRF_APB*/ +#define CRF_BASE 0xFD1A0000 +#define CRF_SIZE 0x00600000 + +/* CRF registers and bitfields */ +#define CRF_RST_APU (CRF_BASE + 0X00000300) + +#define CRF_RST_APU_ACPU_RESET (1 << 0) +#define CRF_RST_APU_ACPU_PWRON_RESET (1 << 10) + +/* APU registers and bitfields */ +#define FPD_APU_BASE 0xFD5C0000U +#define FPD_APU_CONFIG_0 (FPD_APU_BASE + 0x20U) +#define FPD_APU_RVBAR_L_0 (FPD_APU_BASE + 0x40U) +#define FPD_APU_RVBAR_H_0 (FPD_APU_BASE + 0x44U) +#define FPD_APU_PWRCTL (FPD_APU_BASE + 0x90U) + +#define FPD_APU_CONFIG_0_VINITHI_SHIFT 8U +#define APU_0_PWRCTL_CPUPWRDWNREQ_MASK 1U +#define APU_1_PWRCTL_CPUPWRDWNREQ_MASK 2U + +/* PMC registers and bitfields */ +#define PMC_GLOBAL_BASE 0xF1110000U +#define PMC_GLOBAL_GLOB_GEN_STORAGE4 (PMC_GLOBAL_BASE + 0x40U) + +#endif /* VERSAL_DEF_H */ diff --git a/plat/xilinx/versal/plat_psci.c b/plat/xilinx/versal/plat_psci.c new file mode 100644 index 0000000..56d98f7 --- /dev/null +++ b/plat/xilinx/versal/plat_psci.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <plat_arm.h> + +#include <plat_private.h> +#include "pm_api_sys.h" +#include "pm_client.h" +#include <pm_common.h> + +static uintptr_t versal_sec_entry; + +static int32_t versal_pwr_domain_on(u_register_t mpidr) +{ + int32_t cpu_id = plat_core_pos_by_mpidr(mpidr); + const struct pm_proc *proc; + + VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); + + if (cpu_id == -1) { + return PSCI_E_INTERN_FAIL; + } + + proc = pm_get_proc((uint32_t)cpu_id); + + /* Send request to PMC to wake up selected ACPU core */ + (void)pm_req_wakeup(proc->node_id, (versal_sec_entry & 0xFFFFFFFFU) | 0x1U, + versal_sec_entry >> 32, 0, SECURE_FLAG); + + /* Clear power down request */ + pm_client_wakeup(proc); + + return PSCI_E_SUCCESS; +} + +/** + * versal_pwr_domain_suspend() - This function sends request to PMC to suspend + * core. + * @target_state: Targated state. + * + */ +static void versal_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + uint32_t state; + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + + plat_versal_gic_cpuif_disable(); + + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + plat_versal_gic_save(); + } + + state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? + PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; + + /* Send request to PMC to suspend this core */ + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_sec_entry, + SECURE_FLAG); + + /* APU is to be turned off */ + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + /* disable coherency */ + plat_arm_interconnect_exit_coherency(); + } +} + +/** + * versal_pwr_domain_suspend_finish() - This function performs actions to finish + * suspend procedure. + * @target_state: Targated state. + * + */ +static void versal_pwr_domain_suspend_finish( + const psci_power_state_t *target_state) +{ + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + + /* Clear the APU power control register for this cpu */ + pm_client_wakeup(proc); + + /* enable coherency */ + plat_arm_interconnect_enter_coherency(); + + /* APU was turned off, so restore GIC context */ + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + plat_versal_gic_resume(); + } + + plat_versal_gic_cpuif_enable(); +} + +void versal_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + /* Enable the gic cpu interface */ + plat_versal_gic_pcpu_init(); + + /* Program the gic per-cpu distributor or re-distributor interface */ + plat_versal_gic_cpuif_enable(); +} + +/** + * versal_system_off() - This function sends the system off request to firmware. + * This function does not return. + * + */ +static void __dead2 versal_system_off(void) +{ + /* Send the power down request to the PMC */ + (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN, + pm_get_shutdown_scope(), SECURE_FLAG); + + while (1) { + wfi(); + } +} + +/** + * versal_system_reset() - This function sends the reset request to firmware + * for the system to reset. This function does not + * return. + * + */ +static void __dead2 versal_system_reset(void) +{ + /* Send the system reset request to the PMC */ + (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, + pm_get_shutdown_scope(), SECURE_FLAG); + + while (1) { + wfi(); + } +} + +/** + * versal_pwr_domain_off() - This function performs actions to turn off core. + * @target_state: Targated state. + * + */ +static void versal_pwr_domain_off(const psci_power_state_t *target_state) +{ + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + + /* Prevent interrupts from spuriously waking up this cpu */ + plat_versal_gic_cpuif_disable(); + + /* + * Send request to PMC to power down the appropriate APU CPU + * core. + * According to PSCI specification, CPU_off function does not + * have resume address and CPU core can only be woken up + * invoking CPU_on function, during which resume address will + * be set. + */ + (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0, + SECURE_FLAG); +} + +/** + * versal_validate_power_state() - This function ensures that the power state + * parameter in request is valid. + * @power_state: Power state of core. + * @req_state: Requested state. + * + * Return: Returns status, either success or reason. + * + */ +static int32_t versal_validate_power_state(uint32_t power_state, + psci_power_state_t *req_state) +{ + VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); + + uint32_t pstate = psci_get_pstate_type(power_state); + + assert(req_state); + + /* Sanity check the requested state */ + if (pstate == PSTATE_TYPE_STANDBY) { + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; + } else { + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; + } + + /* We expect the 'state id' to be zero */ + if (psci_get_pstate_id(power_state) != 0U) { + return PSCI_E_INVALID_PARAMS; + } + + return PSCI_E_SUCCESS; +} + +/** + * versal_get_sys_suspend_power_state() - Get power state for system suspend. + * @req_state: Requested state. + * + */ +static void versal_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; + req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; +} + +static const struct plat_psci_ops versal_nopmc_psci_ops = { + .pwr_domain_on = versal_pwr_domain_on, + .pwr_domain_off = versal_pwr_domain_off, + .pwr_domain_on_finish = versal_pwr_domain_on_finish, + .pwr_domain_suspend = versal_pwr_domain_suspend, + .pwr_domain_suspend_finish = versal_pwr_domain_suspend_finish, + .system_off = versal_system_off, + .system_reset = versal_system_reset, + .validate_power_state = versal_validate_power_state, + .get_sys_suspend_power_state = versal_get_sys_suspend_power_state, +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint, + const struct plat_psci_ops **psci_ops) +{ + versal_sec_entry = sec_entrypoint; + + *psci_ops = &versal_nopmc_psci_ops; + + return 0; +} diff --git a/plat/xilinx/versal/plat_topology.c b/plat/xilinx/versal/plat_topology.c new file mode 100644 index 0000000..5f38599 --- /dev/null +++ b/plat/xilinx/versal/plat_topology.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <platform_def.h> + +static const uint8_t plat_power_domain_tree_desc[] = {1, PLATFORM_CORE_COUNT}; + +const uint8_t *plat_get_power_domain_tree_desc(void) +{ + return plat_power_domain_tree_desc; +} diff --git a/plat/xilinx/versal/plat_versal.c b/plat/xilinx/versal/plat_versal.c new file mode 100644 index 0000000..ba17b1d --- /dev/null +++ b/plat/xilinx/versal/plat_versal.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/common/platform.h> + +#include <plat_private.h> + +int32_t plat_core_pos_by_mpidr(u_register_t mpidr) +{ + if ((mpidr & MPIDR_CLUSTER_MASK) != 0U) { + return -1; + } + + if ((mpidr & MPIDR_CPU_MASK) >= PLATFORM_CORE_COUNT) { + return -1; + } + + return (int32_t)versal_calc_core_pos(mpidr); +} diff --git a/plat/xilinx/versal/platform.mk b/plat/xilinx/versal/platform.mk new file mode 100644 index 0000000..7c53daa --- /dev/null +++ b/plat/xilinx/versal/platform.mk @@ -0,0 +1,118 @@ +# Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +override PROGRAMMABLE_RESET_ADDRESS := 1 +PSCI_EXTENDED_STATE_ID := 1 +A53_DISABLE_NON_TEMPORAL_HINT := 0 +SEPARATE_CODE_AND_RODATA := 1 +override RESET_TO_BL31 := 1 +PL011_GENERIC_UART := 1 +IPI_CRC_CHECK := 0 +HARDEN_SLS_ALL := 0 + +# A72 Erratum for SoC +ERRATA_A72_859971 := 1 +ERRATA_A72_1319367 := 1 + +ifdef VERSAL_ATF_MEM_BASE + $(eval $(call add_define,VERSAL_ATF_MEM_BASE)) + + ifndef VERSAL_ATF_MEM_SIZE + $(error "VERSAL_ATF_BASE defined without VERSAL_ATF_SIZE") + endif + $(eval $(call add_define,VERSAL_ATF_MEM_SIZE)) + + ifdef VERSAL_ATF_MEM_PROGBITS_SIZE + $(eval $(call add_define,VERSAL_ATF_MEM_PROGBITS_SIZE)) + endif +endif + +ifdef VERSAL_BL32_MEM_BASE + $(eval $(call add_define,VERSAL_BL32_MEM_BASE)) + + ifndef VERSAL_BL32_MEM_SIZE + $(error "VERSAL_BL32_BASE defined without VERSAL_BL32_SIZE") + endif + $(eval $(call add_define,VERSAL_BL32_MEM_SIZE)) +endif + +ifdef IPI_CRC_CHECK + $(eval $(call add_define,IPI_CRC_CHECK)) +endif + +VERSAL_PLATFORM ?= silicon +$(eval $(call add_define_val,VERSAL_PLATFORM,VERSAL_PLATFORM_ID_${VERSAL_PLATFORM})) + +ifdef XILINX_OF_BOARD_DTB_ADDR +$(eval $(call add_define,XILINX_OF_BOARD_DTB_ADDR)) +endif + +PLAT_XLAT_TABLES_DYNAMIC := 0 +ifeq (${PLAT_XLAT_TABLES_DYNAMIC},1) +$(eval $(call add_define,PLAT_XLAT_TABLES_DYNAMIC)) +endif + +# enable assert() for release/debug builds +ENABLE_ASSERTIONS := 1 + +PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ + -Iplat/xilinx/common/include/ \ + -Iplat/xilinx/common/ipi_mailbox_service/ \ + -Iplat/xilinx/versal/include/ \ + -Iplat/xilinx/versal/pm_service/ + +include lib/libfdt/libfdt.mk +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk +include lib/xlat_tables_v2/xlat_tables.mk + +PLAT_BL_COMMON_SOURCES := drivers/arm/dcc/dcc_console.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + ${GICV3_SOURCES} \ + drivers/arm/pl011/aarch64/pl011_console.S \ + plat/common/aarch64/crash_console_helpers.S \ + plat/arm/common/arm_cci.c \ + plat/arm/common/arm_common.c \ + plat/common/plat_gicv3.c \ + plat/xilinx/versal/aarch64/versal_helpers.S \ + plat/xilinx/versal/aarch64/versal_common.c \ + ${XLAT_TABLES_LIB_SRCS} + +VERSAL_CONSOLE ?= pl011 +ifeq (${VERSAL_CONSOLE}, $(filter ${VERSAL_CONSOLE},pl011 pl011_0 pl011_1 dcc)) +else + $(error "Please define VERSAL_CONSOLE") +endif + +$(eval $(call add_define_val,VERSAL_CONSOLE,VERSAL_CONSOLE_ID_${VERSAL_CONSOLE})) + +BL31_SOURCES += drivers/arm/cci/cci.c \ + lib/cpus/aarch64/cortex_a72.S \ + common/fdt_wrappers.c \ + plat/common/plat_psci_common.c \ + plat/xilinx/common/ipi.c \ + plat/xilinx/common/plat_fdt.c \ + plat/xilinx/common/plat_console.c \ + plat/xilinx/common/plat_startup.c \ + plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ + plat/xilinx/common/pm_service/pm_ipi.c \ + plat/xilinx/common/pm_service/pm_api_sys.c \ + plat/xilinx/common/pm_service/pm_svc_main.c \ + plat/xilinx/common/versal.c \ + plat/xilinx/versal/bl31_versal_setup.c \ + plat/xilinx/versal/plat_psci.c \ + plat/xilinx/versal/plat_versal.c \ + plat/xilinx/versal/plat_topology.c \ + plat/xilinx/versal/sip_svc_setup.c \ + plat/xilinx/versal/versal_gicv3.c \ + plat/xilinx/versal/versal_ipi.c \ + plat/xilinx/versal/pm_service/pm_client.c \ + common/fdt_fixup.c \ + ${LIBFDT_SRCS} + +ifeq ($(HARDEN_SLS_ALL), 1) +TF_CFLAGS_aarch64 += -mharden-sls=all +endif diff --git a/plat/xilinx/versal/pm_service/pm_client.c b/plat/xilinx/versal/pm_service/pm_client.c new file mode 100644 index 0000000..ccbfe77 --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_client.c @@ -0,0 +1,280 @@ +/* + * 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 + */ + +/* + * APU specific definition of processors in the subsystem as well as functions + * for getting information about and changing state of the APU. + */ + +#include <assert.h> + +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv3.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <lib/utils.h> +#include <plat/common/platform.h> + +#include <plat_ipi.h> +#include <platform_def.h> +#include "pm_api_sys.h" +#include "pm_client.h" +#include "pm_defs.h" +#include <versal_def.h> + +#define UNDEFINED_CPUID (~0) + +DEFINE_BAKERY_LOCK(pm_client_secure_lock); + +static const struct pm_ipi apu_ipi = { + .local_ipi_id = IPI_LOCAL_ID, + .remote_ipi_id = IPI_REMOTE_ID, + .buffer_base = IPI_BUFFER_LOCAL_BASE, +}; + +/* Order in pm_procs_all array must match cpu ids */ +static const struct pm_proc pm_procs_all[] = { + { + .node_id = XPM_DEVID_ACPU_0, + .ipi = &apu_ipi, + .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK, + }, + { + .node_id = XPM_DEVID_ACPU_1, + .ipi = &apu_ipi, + .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK, + } +}; + +const struct pm_proc *primary_proc = &pm_procs_all[0]; + +/** + * irq_to_pm_node_idx - Get PM node index corresponding to the interrupt number. + * @irq: Interrupt number + * + * Return: PM node index corresponding to the specified interrupt. + * + */ +enum pm_device_node_idx irq_to_pm_node_idx(uint32_t irq) +{ + enum pm_device_node_idx dev_idx = XPM_NODEIDX_DEV_MIN; + + assert(irq <= IRQ_MAX); + + switch (irq) { + case 13: + dev_idx = XPM_NODEIDX_DEV_GPIO; + break; + case 14: + dev_idx = XPM_NODEIDX_DEV_I2C_0; + break; + case 15: + dev_idx = XPM_NODEIDX_DEV_I2C_1; + break; + case 16: + dev_idx = XPM_NODEIDX_DEV_SPI_0; + break; + case 17: + dev_idx = XPM_NODEIDX_DEV_SPI_1; + break; + case 18: + dev_idx = XPM_NODEIDX_DEV_UART_0; + break; + case 19: + dev_idx = XPM_NODEIDX_DEV_UART_1; + break; + case 20: + dev_idx = XPM_NODEIDX_DEV_CAN_FD_0; + break; + case 21: + dev_idx = XPM_NODEIDX_DEV_CAN_FD_1; + break; + case 22: + case 23: + case 24: + case 25: + case 26: + dev_idx = XPM_NODEIDX_DEV_USB_0; + break; + case 37: + case 38: + case 39: + dev_idx = XPM_NODEIDX_DEV_TTC_0; + break; + case 40: + case 41: + case 42: + dev_idx = XPM_NODEIDX_DEV_TTC_1; + break; + case 43: + case 44: + case 45: + dev_idx = XPM_NODEIDX_DEV_TTC_2; + break; + case 46: + case 47: + case 48: + dev_idx = XPM_NODEIDX_DEV_TTC_3; + break; + case 56: + case 57: + dev_idx = XPM_NODEIDX_DEV_GEM_0; + break; + case 58: + case 59: + dev_idx = XPM_NODEIDX_DEV_GEM_1; + break; + case 60: + dev_idx = XPM_NODEIDX_DEV_ADMA_0; + break; + case 61: + dev_idx = XPM_NODEIDX_DEV_ADMA_1; + break; + case 62: + dev_idx = XPM_NODEIDX_DEV_ADMA_2; + break; + case 63: + dev_idx = XPM_NODEIDX_DEV_ADMA_3; + break; + case 64: + dev_idx = XPM_NODEIDX_DEV_ADMA_4; + break; + case 65: + dev_idx = XPM_NODEIDX_DEV_ADMA_5; + break; + case 66: + dev_idx = XPM_NODEIDX_DEV_ADMA_6; + break; + case 67: + dev_idx = XPM_NODEIDX_DEV_ADMA_7; + break; + case 74: + dev_idx = XPM_NODEIDX_DEV_USB_0; + break; + case 126: + case 127: + dev_idx = XPM_NODEIDX_DEV_SDIO_0; + break; + case 128: + case 129: + dev_idx = XPM_NODEIDX_DEV_SDIO_1; + break; + case 142: + dev_idx = XPM_NODEIDX_DEV_RTC; + break; + default: + dev_idx = XPM_NODEIDX_DEV_MIN; + break; + } + + return dev_idx; +} + +/** + * pm_client_suspend() - Client-specific suspend actions. + * @proc: processor which need to suspend. + * @state: desired suspend state. + * + * This function should contain any PU-specific actions + * required prior to sending suspend request to PMU + * Actions taken depend on the state system is suspending to. + * + */ +void pm_client_suspend(const struct pm_proc *proc, uint32_t state) +{ + bakery_lock_get(&pm_client_secure_lock); + + if (state == PM_STATE_SUSPEND_TO_RAM) { + pm_client_set_wakeup_sources((uint32_t)proc->node_id); + } + + /* Set powerdown request */ + mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) | + (uint32_t)proc->pwrdn_mask); + + bakery_lock_release(&pm_client_secure_lock); +} + +/** + * pm_client_abort_suspend() - Client-specific abort-suspend actions. + * + * This function should contain any PU-specific actions + * required for aborting a prior suspend request. + * + */ +void pm_client_abort_suspend(void) +{ + /* Enable interrupts at processor level (for current cpu) */ + gicv3_cpuif_enable(plat_my_core_pos()); + + bakery_lock_get(&pm_client_secure_lock); + + /* Clear powerdown request */ + mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) & + ~((uint32_t)primary_proc->pwrdn_mask)); + + bakery_lock_release(&pm_client_secure_lock); +} + +/** + * pm_get_cpuid() - get the local cpu ID for a global node ID. + * @nid: node id of the processor. + * + * Return: the cpu ID (starting from 0) for the subsystem. + * + */ +static uint32_t pm_get_cpuid(uint32_t nid) +{ + for (size_t i = 0U; i < ARRAY_SIZE(pm_procs_all); i++) { + if (pm_procs_all[i].node_id == nid) { + return i; + } + } + return UNDEFINED_CPUID; +} + +/** + * pm_client_wakeup() - Client-specific wakeup actions. + * @proc: Processor which need to wakeup. + * + * This function should contain any PU-specific actions + * required for waking up another APU core. + * + */ +void pm_client_wakeup(const struct pm_proc *proc) +{ + uint32_t cpuid = pm_get_cpuid(proc->node_id); + + if (cpuid == UNDEFINED_CPUID) { + return; + } + + bakery_lock_get(&pm_client_secure_lock); + + /* clear powerdown bit for affected cpu */ + uint32_t val = mmio_read_32(FPD_APU_PWRCTL); + val &= ~(proc->pwrdn_mask); + mmio_write_32(FPD_APU_PWRCTL, val); + + bakery_lock_release(&pm_client_secure_lock); +} + +/** + * pm_get_proc() - returns pointer to the proc structure. + * @cpuid: id of the cpu whose proc struct pointer should be returned. + * + * Return: pointer to a proc structure if proc is found, otherwise NULL. + * + */ +const struct pm_proc *pm_get_proc(uint32_t cpuid) +{ + if (cpuid < ARRAY_SIZE(pm_procs_all)) { + return &pm_procs_all[cpuid]; + } + + return NULL; +} diff --git a/plat/xilinx/versal/sip_svc_setup.c b/plat/xilinx/versal/sip_svc_setup.c new file mode 100644 index 0000000..b30254d --- /dev/null +++ b/plat/xilinx/versal/sip_svc_setup.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018-2021, 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 SiP calls. Dispatch PM calls to PM SMC handler. */ + +#include <inttypes.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <tools_share/uuid.h> + +#include "ipi_mailbox_svc.h" +#include "pm_svc_main.h" + +/* SMC function IDs for SiP Service queries */ +#define VERSAL_SIP_SVC_CALL_COUNT U(0x8200ff00) +#define VERSAL_SIP_SVC_UID U(0x8200ff01) +#define VERSAL_SIP_SVC_VERSION U(0x8200ff03) + +/* SiP Service Calls version numbers */ +#define SIP_SVC_VERSION_MAJOR U(0) +#define SIP_SVC_VERSION_MINOR U(1) + +/* These macros are used to identify PM calls from the SMC function ID */ +#define SIP_FID_MASK GENMASK(23, 16) +#define XLNX_FID_MASK GENMASK(23, 12) +#define PM_FID_VALUE 0u +#define IPI_FID_VALUE 0x1000u +#define is_pm_fid(_fid) (((_fid) & XLNX_FID_MASK) == PM_FID_VALUE) +#define is_ipi_fid(_fid) (((_fid) & XLNX_FID_MASK) == IPI_FID_VALUE) + +/* SiP Service UUID */ +DEFINE_SVC_UUID2(versal_sip_uuid, + 0x2ab9e4ecU, 0x93b9U, 0x11e7U, 0xa0U, 0x19U, + 0xdfU, 0xe0U, 0xdbU, 0xadU, 0x0aU, 0xe0U); + +/** + * sip_svc_setup() - Setup SiP Service + * + * Return: 0 on success,negative error code on failure. + * + * Invokes PM setup. + */ +static int32_t sip_svc_setup(void) +{ + /* PM implementation as SiP Service */ + (void)pm_setup(); + + return 0; +} + +/** + * sip_svc_smc_handler() - Top-level SiP Service SMC handler. + * @smc_fid: Function Identifier. + * @x1: SMC64 Arguments 1 from kernel. + * @x2: SMC64 Arguments 2 from kernel. + * @x3: SMC64 Arguments 3 from kernel(upper 32-bits). + * @x4: SMC64 Arguments 4 from kernel. + * @cookie: Unused + * @handle: Pointer to caller's context structure. + * @flags: SECURE_FLAG or NON_SECURE_FLAG. + * + * Handler for all SiP SMC calls. Handles standard SIP requests + * and calls PM SMC handler if the call is for a PM-API function. + * + * Return: Unused. + */ +uintptr_t sip_svc_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + VERBOSE("SMCID: 0x%08x, x1: 0x%016" PRIx64 ", x2: 0x%016" PRIx64 ", x3: 0x%016" PRIx64 ", x4: 0x%016" PRIx64 "\n", + smc_fid, x1, x2, x3, x4); + + if (smc_fid & SIP_FID_MASK) { + WARN("SMC out of SiP assinged range: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } + + /* Let PM SMC handler deal with PM-related requests */ + if (is_pm_fid(smc_fid)) { + return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } + + /* Let IPI SMC handler deal with IPI-related requests */ + if (is_ipi_fid(smc_fid)) { + return ipi_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } + + /* Let PM SMC handler deal with PM-related requests */ + switch (smc_fid) { + case VERSAL_SIP_SVC_CALL_COUNT: + /* PM functions + default functions */ + SMC_RET1(handle, 2); + + case VERSAL_SIP_SVC_UID: + SMC_UUID_RET(handle, versal_sip_uuid); + + case VERSAL_SIP_SVC_VERSION: + SMC_RET2(handle, SIP_SVC_VERSION_MAJOR, SIP_SVC_VERSION_MINOR); + + default: + WARN("Unimplemented SiP Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +/* Register PM Service Calls as runtime service */ +DECLARE_RT_SVC( + sip_svc, + OEN_SIP_START, + OEN_SIP_END, + SMC_TYPE_FAST, + sip_svc_setup, + sip_svc_smc_handler); diff --git a/plat/xilinx/versal/tsp/tsp-versal.mk b/plat/xilinx/versal/tsp/tsp-versal.mk new file mode 100644 index 0000000..bf32de3 --- /dev/null +++ b/plat/xilinx/versal/tsp/tsp-versal.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# TSP source files specific to Versal platform + +PLAT_XILINX_COMMON := plat/xilinx/common/ + +include ${PLAT_XILINX_COMMON}/tsp/tsp.mk diff --git a/plat/xilinx/versal/versal_gicv3.c b/plat/xilinx/versal/versal_gicv3.c new file mode 100644 index 0000000..197d047 --- /dev/null +++ b/plat/xilinx/versal/versal_gicv3.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018-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 + */ + +#include <common/interrupt_props.h> +#include <drivers/arm/gicv3.h> +#include <lib/utils.h> +#include <plat/common/platform.h> + +#include <plat_private.h> +#include <platform_def.h> + + +/****************************************************************************** + * The following functions are defined as weak to allow a platform to override + * the way the GICv3 driver is initialised and used. + *****************************************************************************/ +#pragma weak plat_versal_gic_driver_init +#pragma weak plat_versal_gic_init +#pragma weak plat_versal_gic_cpuif_enable +#pragma weak plat_versal_gic_cpuif_disable +#pragma weak plat_versal_gic_pcpu_init +#pragma weak plat_versal_gic_redistif_on +#pragma weak plat_versal_gic_redistif_off + +/* The GICv3 driver only needs to be initialized in EL3 */ +static uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT]; + +static const interrupt_prop_t versal_interrupt_props[] = { + PLAT_VERSAL_G1S_IRQ_PROPS(INTR_GROUP1S), + PLAT_VERSAL_G0_IRQ_PROPS(INTR_GROUP0) +}; + +/* + * We save and restore the GICv3 context on system suspend. Allocate the + * data in the designated EL3 Secure carve-out memory. + */ +static gicv3_redist_ctx_t rdist_ctx __section(".versal_el3_tzc_dram"); +static gicv3_dist_ctx_t dist_ctx __section(".versal_el3_tzc_dram"); + +/* + * MPIDR hashing function for translating MPIDRs read from GICR_TYPER register + * to core position. + * + * Calculating core position is dependent on MPIDR_EL1.MT bit. However, affinity + * values read from GICR_TYPER don't have an MT field. To reuse the same + * translation used for CPUs, we insert MT bit read from the PE's MPIDR into + * that read from GICR_TYPER. + * + * Assumptions: + * + * - All CPUs implemented in the system have MPIDR_EL1.MT bit set; + * - No CPUs implemented in the system use affinity level 3. + */ +static uint32_t versal_gicv3_mpidr_hash(u_register_t mpidr) +{ + mpidr |= (read_mpidr_el1() & MPIDR_MT_MASK); + return versal_calc_core_pos(mpidr); +} + +static const gicv3_driver_data_t versal_gic_data __unused = { + .gicd_base = PLAT_GICD_BASE_VALUE, + .gicr_base = PLAT_GICR_BASE_VALUE, + .interrupt_props = versal_interrupt_props, + .interrupt_props_num = ARRAY_SIZE(versal_interrupt_props), + .rdistif_num = PLATFORM_CORE_COUNT, + .rdistif_base_addrs = rdistif_base_addrs, + .mpidr_to_core_pos = versal_gicv3_mpidr_hash +}; + +void __init plat_versal_gic_driver_init(void) +{ + /* + * The GICv3 driver is initialized in EL3 and does not need + * to be initialized again in SEL1. This is because the S-EL1 + * can use GIC system registers to manage interrupts and does + * not need GIC interface base addresses to be configured. + */ +#if IMAGE_BL31 + gicv3_driver_init(&versal_gic_data); +#endif +} + +/****************************************************************************** + * Versal common helper to initialize the GIC. Only invoked by BL31 + *****************************************************************************/ +void __init plat_versal_gic_init(void) +{ + gicv3_distif_init(); + gicv3_rdistif_init(plat_my_core_pos()); + gicv3_cpuif_enable(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal common helper to enable the GIC CPU interface + *****************************************************************************/ +void plat_versal_gic_cpuif_enable(void) +{ + gicv3_cpuif_enable(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal common helper to disable the GIC CPU interface + *****************************************************************************/ +void plat_versal_gic_cpuif_disable(void) +{ + gicv3_cpuif_disable(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal common helper to initialize the per-cpu redistributor interface in + * GICv3 + *****************************************************************************/ +void plat_versal_gic_pcpu_init(void) +{ + gicv3_rdistif_init(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal common helpers to power GIC redistributor interface + *****************************************************************************/ +void plat_versal_gic_redistif_on(void) +{ + gicv3_rdistif_on(plat_my_core_pos()); +} + +void plat_versal_gic_redistif_off(void) +{ + gicv3_rdistif_off(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal common helper to save & restore the GICv3 on resume from system + * suspend + *****************************************************************************/ +void plat_versal_gic_save(void) +{ + /* + * If an ITS is available, save its context before + * the Redistributor using: + * gicv3_its_save_disable(gits_base, &its_ctx[i]) + * Additionnaly, an implementation-defined sequence may + * be required to save the whole ITS state. + */ + + /* + * Save the GIC Redistributors and ITS contexts before the + * Distributor context. As we only handle SYSTEM SUSPEND API, + * we only need to save the context of the CPU that is issuing + * the SYSTEM SUSPEND call, i.e. the current CPU. + */ + gicv3_rdistif_save(plat_my_core_pos(), &rdist_ctx); + + /* Save the GIC Distributor context */ + gicv3_distif_save(&dist_ctx); + + /* + * From here, all the components of the GIC can be safely powered down + * as long as there is an alternate way to handle wakeup interrupt + * sources. + */ +} + +void plat_versal_gic_resume(void) +{ + /* Restore the GIC Distributor context */ + gicv3_distif_init_restore(&dist_ctx); + + /* + * Restore the GIC Redistributor and ITS contexts after the + * Distributor context. As we only handle SYSTEM SUSPEND API, + * we only need to restore the context of the CPU that issued + * the SYSTEM SUSPEND call. + */ + gicv3_rdistif_init_restore(plat_my_core_pos(), &rdist_ctx); + + /* + * If an ITS is available, restore its context after + * the Redistributor using: + * gicv3_its_restore(gits_base, &its_ctx[i]) + * An implementation-defined sequence may be required to + * restore the whole ITS state. The ITS must also be + * re-enabled after this sequence has been executed. + */ +} diff --git a/plat/xilinx/versal/versal_ipi.c b/plat/xilinx/versal/versal_ipi.c new file mode 100644 index 0000000..74b082d --- /dev/null +++ b/plat/xilinx/versal/versal_ipi.c @@ -0,0 +1,77 @@ +/* + * 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 IPI agent registers access management + */ + +#include <lib/utils_def.h> + +#include <ipi.h> +#include <plat_ipi.h> + +/* versal ipi configuration table */ +static const struct ipi_config versal_ipi_table[] = { + /* PMC IPI */ + [IPI_ID_PMC] = { + .ipi_bit_mask = PMC_IPI_TRIG_BIT, + .ipi_reg_base = PMC_REG_BASE, + .secure_only = IPI_SECURE_MASK, + }, + + /* A72 IPI */ + [IPI_ID_APU] = { + .ipi_bit_mask = IPI0_TRIG_BIT, + .ipi_reg_base = IPI0_REG_BASE, + .secure_only = 0U, + }, + + /* RPU0 IPI */ + [IPI_ID_RPU0] = { + .ipi_bit_mask = IPI1_TRIG_BIT, + .ipi_reg_base = IPI1_REG_BASE, + .secure_only = 0U, + }, + + /* RPU1 IPI */ + [IPI_ID_RPU1] = { + .ipi_bit_mask = IPI2_TRIG_BIT, + .ipi_reg_base = IPI2_REG_BASE, + .secure_only = 0U, + }, + + /* IPI3 IPI */ + [IPI_ID_3] = { + .ipi_bit_mask = IPI3_TRIG_BIT, + .ipi_reg_base = IPI3_REG_BASE, + .secure_only = 0U, + }, + + /* IPI4 IPI */ + [IPI_ID_4] = { + .ipi_bit_mask = IPI4_TRIG_BIT, + .ipi_reg_base = IPI4_REG_BASE, + .secure_only = 0U, + }, + + /* IPI5 IPI */ + [IPI_ID_5] = { + .ipi_bit_mask = IPI5_TRIG_BIT, + .ipi_reg_base = IPI5_REG_BASE, + .secure_only = 0U, + }, +}; + +/* versal_ipi_config_table_init() - Initialize versal IPI configuration data. + * @ipi_config_table: IPI configuration table. + * @ipi_total: Total number of IPI available. + * + */ +void versal_ipi_config_table_init(void) +{ + ipi_config_table_init(versal_ipi_table, ARRAY_SIZE(versal_ipi_table)); +} diff --git a/plat/xilinx/versal_net/aarch64/versal_net_common.c b/plat/xilinx/versal_net/aarch64/versal_net_common.c new file mode 100644 index 0000000..69c5c87 --- /dev/null +++ b/plat/xilinx/versal_net/aarch64/versal_net_common.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <drivers/generic_delay_timer.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> +#include <plat_common.h> +#include <plat_ipi.h> + +#include <plat_private.h> +#include <versal_net_def.h> + +uint32_t platform_id, platform_version; + +/* + * Table of regions to map using the MMU. + * This doesn't include TZRAM as the 'mem_layout' argument passed to + * configure_mmu_elx() will give the available subset of that, + */ +const mmap_region_t plat_versal_net_mmap[] = { + MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(DEVICE2_BASE, DEVICE2_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(CRF_BASE, CRF_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(IPI_BASE, IPI_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + { 0 } +}; + +const mmap_region_t *plat_get_mmap(void) +{ + return plat_versal_net_mmap; +} + +/* For saving cpu clock for certain platform */ +uint32_t cpu_clock; + +char *board_name_decode(void) +{ + switch (platform_id) { + case VERSAL_NET_SPP: + return "IPP"; + case VERSAL_NET_EMU: + return "EMU"; + case VERSAL_NET_SILICON: + return "Silicon"; + case VERSAL_NET_QEMU: + return "QEMU"; + default: + return "Unknown"; + } +} + +void board_detection(void) +{ + uint32_t version; + + version = mmio_read_32(PMC_TAP_VERSION); + platform_id = FIELD_GET(PLATFORM_MASK, version); + platform_version = FIELD_GET(PLATFORM_VERSION_MASK, version); + + if (platform_id == VERSAL_NET_QEMU_COSIM) { + platform_id = VERSAL_NET_QEMU; + } + + if ((platform_id == VERSAL_NET_SPP) || + (platform_id == VERSAL_NET_EMU) || + (platform_id == VERSAL_NET_QEMU)) { + /* + * 9 is diff for + * 0 means 0.9 version + * 1 means 1.0 version + * 2 means 1.1 version + * etc, + */ + platform_version += 9U; + } + + /* Make sure that console is setup to see this message */ + VERBOSE("Platform id: %d version: %d.%d\n", platform_id, + platform_version / 10U, platform_version % 10U); +} + +uint32_t get_uart_clk(void) +{ + uint32_t uart_clock; + + switch (platform_id) { + case VERSAL_NET_SPP: + uart_clock = 1000000; + break; + case VERSAL_NET_EMU: + uart_clock = 25000000; + break; + case VERSAL_NET_QEMU: + uart_clock = 25000000; + break; + case VERSAL_NET_SILICON: + uart_clock = 100000000; + break; + default: + panic(); + } + + return uart_clock; +} + +void versal_net_config_setup(void) +{ + uint32_t val; + uintptr_t crl_base, iou_scntrs_base, psx_base; + + crl_base = VERSAL_NET_CRL; + iou_scntrs_base = VERSAL_NET_IOU_SCNTRS; + psx_base = PSX_CRF; + + /* Reset for system timestamp generator in FPX */ + mmio_write_32(psx_base + PSX_CRF_RST_TIMESTAMP_OFFSET, 0); + + /* Global timer init - Program time stamp reference clk */ + val = mmio_read_32(crl_base + VERSAL_NET_CRL_TIMESTAMP_REF_CTRL_OFFSET); + val |= VERSAL_NET_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT_BIT; + mmio_write_32(crl_base + VERSAL_NET_CRL_TIMESTAMP_REF_CTRL_OFFSET, val); + + /* Clear reset of timestamp reg */ + mmio_write_32(crl_base + VERSAL_NET_CRL_RST_TIMESTAMP_OFFSET, 0); + + /* Program freq register in System counter and enable system counter. */ + mmio_write_32(iou_scntrs_base + VERSAL_NET_IOU_SCNTRS_BASE_FREQ_OFFSET, + cpu_clock); + mmio_write_32(iou_scntrs_base + VERSAL_NET_IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET, + VERSAL_NET_IOU_SCNTRS_CONTROL_EN); + + generic_delay_timer_init(); + +#if (TFA_NO_PM == 0) + /* Configure IPI data for versal_net */ + versal_net_ipi_config_table_init(); +#endif +} + +uint32_t plat_get_syscnt_freq2(void) +{ + return cpu_clock; +} diff --git a/plat/xilinx/versal_net/aarch64/versal_net_helpers.S b/plat/xilinx/versal_net/aarch64/versal_net_helpers.S new file mode 100644 index 0000000..dab8717 --- /dev/null +++ b/plat/xilinx/versal_net/aarch64/versal_net_helpers.S @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-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.h> +#include <asm_macros.S> +#include <drivers/arm/gicv3.h> + +#include <platform_def.h> + + .globl plat_secondary_cold_boot_setup + .globl plat_is_my_cpu_primary + .globl platform_mem_init + .globl plat_my_core_pos + + /* ----------------------------------------------------- + * void plat_secondary_cold_boot_setup (void); + * + * This function performs any platform specific actions + * needed for a secondary cpu after a cold reset e.g + * mark the cpu's presence, mechanism to place it in a + * holding pen etc. + * TODO: Should we read the PSYS register to make sure + * that the request has gone through. + * ----------------------------------------------------- + */ +func plat_secondary_cold_boot_setup + mrs x0, mpidr_el1 + + /* + * There is no sane reason to come out of this wfi. This + * cpu will be powered on and reset by the cpu_on pm api + */ + dsb sy + bl plat_panic_handler +endfunc plat_secondary_cold_boot_setup + +func plat_is_my_cpu_primary + mov x9, x30 + bl plat_my_core_pos + cmp x0, #VERSAL_NET_PRIMARY_CPU + cset x0, eq + ret x9 +endfunc plat_is_my_cpu_primary + + /* ----------------------------------------------------- + * unsigned int plat_my_core_pos(void) + * This function uses the plat_core_pos_by_mpidr() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_my_core_pos + mrs x0, mpidr_el1 + b plat_core_pos_by_mpidr +endfunc plat_my_core_pos + + /* --------------------------------------------------------------------- + * We don't need to carry out any memory initialization on Versal NET + * platform. The Secure RAM is accessible straight away. + * --------------------------------------------------------------------- + */ +func platform_mem_init + ret +endfunc platform_mem_init diff --git a/plat/xilinx/versal_net/bl31_versal_net_setup.c b/plat/xilinx/versal_net/bl31_versal_net_setup.c new file mode 100644 index 0000000..56ef27b --- /dev/null +++ b/plat/xilinx/versal_net/bl31_versal_net_setup.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <bl31/bl31.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <plat/common/platform.h> +#include <plat_arm.h> +#include <plat_console.h> + +#include <plat_fdt.h> +#include <plat_private.h> +#include <plat_startup.h> +#include <pm_api_sys.h> +#include <pm_client.h> +#include <pm_ipi.h> +#include <versal_net_def.h> + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +/* + * Return a pointer to the 'entry_point_info' structure of the next image for + * the security state specified. BL33 corresponds to the non-secure image type + * while BL32 corresponds to the secure image type. A NULL pointer is returned + * if the image does not exist. + */ +entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) +{ + assert(sec_state_is_valid(type)); + + if (type == NON_SECURE) { + return &bl33_image_ep_info; + } + + return &bl32_image_ep_info; +} + +/* + * Set the build time defaults,if we can't find any config data. + */ +static inline void bl31_set_default_config(void) +{ + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = arm_get_spsr_for_bl32_entry(); + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); +} + +/* + * Perform any BL31 specific platform actions. Here is an opportunity to copy + * parameters passed by the calling EL (S-EL1 in BL2 & S-EL3 in BL1) before they + * are lost (potentially). This needs to be done before the MMU is initialized + * so that the memory layout can be used while creating page tables. + */ +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ +#if !(TFA_NO_PM) + uint64_t tfa_handoff_addr, buff[HANDOFF_PARAMS_MAX_SIZE] = {0}; + uint32_t payload[PAYLOAD_ARG_CNT], max_size = HANDOFF_PARAMS_MAX_SIZE; + enum pm_ret_status ret_status; +#endif /* !(TFA_NO_PM) */ + + board_detection(); + + switch (platform_id) { + case VERSAL_NET_SPP: + cpu_clock = 1000000; + break; + case VERSAL_NET_EMU: + cpu_clock = 3660000; + break; + case VERSAL_NET_QEMU: + /* Random values now */ + cpu_clock = 100000000; + break; + case VERSAL_NET_SILICON: + cpu_clock = 100000000; + break; + default: + panic(); + } + + setup_console(); + + NOTICE("TF-A running on %s %d.%d\n", board_name_decode(), + platform_version / 10U, platform_version % 10U); + + /* Initialize the platform config for future decision making */ + versal_net_config_setup(); + + /* + * Do initial security configuration to allow DRAM/device access. On + * Base VERSAL_NET only DRAM security is programmable (via TrustZone), but + * other platforms might have more programmable security devices + * present. + */ + + /* Populate common information for BL32 and BL33 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); +#if !(TFA_NO_PM) + PM_PACK_PAYLOAD4(payload, LOADER_MODULE_ID, 1, PM_LOAD_GET_HANDOFF_PARAMS, + (uintptr_t)buff >> 32U, (uintptr_t)buff, max_size); + + ret_status = pm_ipi_send_sync(primary_proc, payload, NULL, 0); + if (ret_status == PM_RET_SUCCESS) { + enum xbl_handoff xbl_ret; + + tfa_handoff_addr = (uintptr_t)&buff; + + xbl_ret = xbl_handover(&bl32_image_ep_info, &bl33_image_ep_info, + tfa_handoff_addr); + if (xbl_ret != XBL_HANDOFF_SUCCESS) { + ERROR("BL31: PLM to TF-A handover failed %u\n", xbl_ret); + panic(); + } + + INFO("BL31: PLM to TF-A handover success\n"); + + /* + * The BL32 load address is indicated as 0x0 in the handoff + * parameters, which is different from the default/user-provided + * load address of 0x60000000 but the flags are correctly + * configured. Consequently, in this scenario, set the PC + * to the requested BL32_BASE address. + */ + + /* TODO: Remove the following check once this is fixed from PLM */ + if (bl32_image_ep_info.pc == 0 && bl32_image_ep_info.spsr != 0) { + bl32_image_ep_info.pc = (uintptr_t)BL32_BASE; + } + } else { + INFO("BL31: setting up default configs\n"); + + bl31_set_default_config(); + } +#else + bl31_set_default_config(); +#endif /* !(TFA_NO_PM) */ + + NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc); + NOTICE("BL31: Non secure code at 0x%lx\n", bl33_image_ep_info.pc); +} + +static versal_intr_info_type_el3_t type_el3_interrupt_table[MAX_INTR_EL3]; + +int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) +{ + static uint32_t index; + uint32_t i; + + /* Validate 'handler' and 'id' parameters */ + if (handler == NULL || index >= MAX_INTR_EL3) { + return -EINVAL; + } + + /* Check if a handler has already been registered */ + for (i = 0; i < index; i++) { + if (id == type_el3_interrupt_table[i].id) { + return -EALREADY; + } + } + + type_el3_interrupt_table[index].id = id; + type_el3_interrupt_table[index].handler = handler; + + index++; + + return 0; +} + +static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + uint32_t intr_id; + uint32_t i; + interrupt_type_handler_t handler = NULL; + + intr_id = plat_ic_get_pending_interrupt_id(); + + for (i = 0; i < MAX_INTR_EL3; i++) { + if (intr_id == type_el3_interrupt_table[i].id) { + handler = type_el3_interrupt_table[i].handler; + } + } + + if (handler != NULL) { + handler(intr_id, flags, handle, cookie); + } + + return 0; +} + +void bl31_platform_setup(void) +{ + prepare_dtb(); + + /* Initialize the gic cpu and distributor interfaces */ + plat_versal_net_gic_driver_init(); + plat_versal_net_gic_init(); +} + +void bl31_plat_runtime_setup(void) +{ + uint64_t flags = 0; + int32_t rc; + + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_EL3, + rdo_el3_interrupt_handler, flags); + if (rc != 0) { + panic(); + } + + console_switch_state(CONSOLE_FLAG_RUNTIME); +} + +/* + * Perform the very early platform specific architectural setup here. + */ +void bl31_plat_arch_setup(void) +{ + const mmap_region_t bl_regions[] = { +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) + MAP_REGION_FLAT(XILINX_OF_BOARD_DTB_ADDR, XILINX_OF_BOARD_DTB_MAX_SIZE, + MT_MEMORY | MT_RW | MT_NS), +#endif + MAP_REGION_FLAT(BL31_BASE, BL31_END - BL31_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), + {0} + }; + + setup_page_tables(bl_regions, plat_get_mmap()); + enable_mmu(0); +} diff --git a/plat/xilinx/versal_net/include/plat_ipi.h b/plat/xilinx/versal_net/include/plat_ipi.h new file mode 100644 index 0000000..9f9947e --- /dev/null +++ b/plat/xilinx/versal_net/include/plat_ipi.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Versal IPI management enums and defines */ + +#ifndef PLAT_IPI_H +#define PLAT_IPI_H + +#include <stdint.h> + +#include <ipi.h> + +/********************************************************************* + * IPI agent IDs macros + ********************************************************************/ +#define IPI_ID_PMC 1U +#define IPI_ID_APU 2U +#define IPI_ID_RPU0 3U +#define IPI_ID_RPU1 4U +#define IPI_ID_3 5U +#define IPI_ID_4 6U +#define IPI_ID_5 7U +#define IPI_ID_MAX 8U + +/********************************************************************* + * IPI message buffers + ********************************************************************/ +#define IPI_BUFFER_BASEADDR (0xEB3F0000U) + +#define IPI_LOCAL_ID IPI_ID_APU +#define IPI_REMOTE_ID IPI_ID_PMC + +#define IPI_BUFFER_LOCAL_BASE (IPI_BUFFER_BASEADDR + (IPI_LOCAL_ID * 0x200U)) +#define IPI_BUFFER_REMOTE_BASE (IPI_BUFFER_BASEADDR + (IPI_REMOTE_ID * 0x200U)) + +#define IPI_BUFFER_TARGET_LOCAL_OFFSET (IPI_LOCAL_ID * 0x40U) +#define IPI_BUFFER_TARGET_REMOTE_OFFSET (IPI_REMOTE_ID * 0x40U) + +#define IPI_BUFFER_MAX_WORDS 8 + +#define IPI_BUFFER_REQ_OFFSET 0x0U +#define IPI_BUFFER_RESP_OFFSET 0x20U + +/********************************************************************* + * Platform specific IPI API declarations + ********************************************************************/ + +/* Configure IPI table for versal_net */ +void versal_net_ipi_config_table_init(void); + +/******************************************************************************* + * IPI registers and bitfields + ******************************************************************************/ +#define IPI0_REG_BASE (0xEB330000U) +#define IPI0_TRIG_BIT (1 << 2) +#define PMC_IPI_TRIG_BIT (1 << 1) +#define IPI1_REG_BASE (0xEB340000U) +#define IPI1_TRIG_BIT (1 << 3) +#define IPI2_REG_BASE (0xEB350000U) +#define IPI2_TRIG_BIT (1 << 4) +#define IPI3_REG_BASE (0xEB360000U) +#define IPI3_TRIG_BIT (1 << 5) +#define IPI4_REG_BASE (0xEB370000U) +#define IPI4_TRIG_BIT (1 << 6) +#define IPI5_REG_BASE (0xEB380000U) +#define IPI5_TRIG_BIT (1 << 7) + +#endif /* PLAT_IPI_H */ diff --git a/plat/xilinx/versal_net/include/plat_macros.S b/plat/xilinx/versal_net/include/plat_macros.S new file mode 100644 index 0000000..db7e42b --- /dev/null +++ b/plat/xilinx/versal_net/include/plat_macros.S @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv2.h> +#include <drivers/arm/gicv3.h> + +#include "../include/platform_def.h" + +.section .rodata.gic_reg_name, "aS" +/* Applicable only to GICv2 and GICv3 with SRE disabled (legacy mode) */ +gicc_regs: + .asciz "gicc_hppir", "gicc_ahppir", "gicc_ctlr", "" + +/* Applicable only to GICv3 with SRE enabled */ +icc_regs: + .asciz "icc_hppir0_el1", "icc_hppir1_el1", "icc_ctlr_el3", "" + +/* Registers common to both GICv2 and GICv3 */ +gicd_pend_reg: + .asciz "gicd_ispendr regs (Offsets 0x200 - 0x278)\n Offset:\t\t\tvalue\n" +newline: + .asciz "\n" +spacer: + .asciz ":\t\t0x" + + /* --------------------------------------------- + * The below utility macro prints out relevant GIC + * registers whenever an unhandled exception is + * taken in BL31 on Versal NET platform. + * Expects: GICD base in x16, GICC base in x17 + * Clobbers: x0 - x10, sp + * --------------------------------------------- + */ + .macro versal_net_print_gic_regs + /* Check for GICv3 system register access */ + mrs x7, id_aa64pfr0_el1 + ubfx x7, x7, #ID_AA64PFR0_GIC_SHIFT, #ID_AA64PFR0_GIC_WIDTH + cmp x7, #1 + b.ne print_gicv2 + + /* Check for SRE enable */ + mrs x8, ICC_SRE_EL3 + tst x8, #ICC_SRE_SRE_BIT + b.eq print_gicv2 + + /* Load the icc reg list to x6 */ + adr x6, icc_regs + /* Load the icc regs to gp regs used by str_in_crash_buf_print */ + mrs x8, ICC_HPPIR0_EL1 + mrs x9, ICC_HPPIR1_EL1 + mrs x10, ICC_CTLR_EL3 + /* Store to the crash buf and print to console */ + bl str_in_crash_buf_print + b print_gic_common + +print_gicv2: + /* Load the gicc reg list to x6 */ + adr x6, gicc_regs + /* Load the gicc regs to gp regs used by str_in_crash_buf_print */ + ldr w8, [x17, #GICC_HPPIR] + ldr w9, [x17, #GICC_AHPPIR] + ldr w10, [x17, #GICC_CTLR] + /* Store to the crash buf and print to console */ + bl str_in_crash_buf_print + +print_gic_common: + /* Print the GICD_ISPENDR regs */ + add x7, x16, #GICD_ISPENDR + adr x4, gicd_pend_reg + bl asm_print_str +gicd_ispendr_loop: + sub x4, x7, x16 + cmp x4, #0x280 + b.eq exit_print_gic_regs + bl asm_print_hex + + adr x4, spacer + bl asm_print_str + + ldr x4, [x7], #8 + bl asm_print_hex + + adr x4, newline + bl asm_print_str + b gicd_ispendr_loop +exit_print_gic_regs: + .endm + + /* --------------------------------------------- + * The below required platform porting macro + * prints out relevant GIC and CCI registers + * whenever an unhandled exception is taken in + * BL31. + * Clobbers: x0 - x10, x16, x17, sp + * --------------------------------------------- + */ + .macro plat_crash_print_regs + /* + * Empty for now to handle more platforms variant. + * Uncomment it when versions are stable + */ + /* + mov_imm x17, PLAT_GICD_BASE_VALUE + mov_imm x16, PLAT_GICR_BASE_VALUE + versal_net_print_gic_regs + */ + .endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/xilinx/versal_net/include/plat_pm_common.h b/plat/xilinx/versal_net/include/plat_pm_common.h new file mode 100644 index 0000000..6485df7 --- /dev/null +++ b/plat/xilinx/versal_net/include/plat_pm_common.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Contains platform specific definitions of commonly used macros data types + * for PU Power Management. This file should be common for all PU's. + */ + +#ifndef PLAT_PM_COMMON_H +#define PLAT_PM_COMMON_H + +#include <stdint.h> + +#include <common/debug.h> + +#include "pm_defs.h" + +#define NON_SECURE_FLAG 1U +#define SECURE_FLAG 0U + +#endif /* PLAT_PM_COMMON_H */ diff --git a/plat/xilinx/versal_net/include/plat_private.h b/plat/xilinx/versal_net/include/plat_private.h new file mode 100644 index 0000000..9cd8636 --- /dev/null +++ b/plat/xilinx/versal_net/include/plat_private.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_PRIVATE_H +#define PLAT_PRIVATE_H + +#include <bl31/interrupt_mgmt.h> +#include <lib/xlat_tables/xlat_tables_v2.h> + +typedef struct versal_intr_info_type_el3 { + uint32_t id; + interrupt_type_handler_t handler; +} versal_intr_info_type_el3_t; + +void versal_net_config_setup(void); +uint32_t get_uart_clk(void); + +const mmap_region_t *plat_get_mmap(void); + +void plat_versal_net_gic_driver_init(void); +void plat_versal_net_gic_init(void); +void plat_versal_net_gic_cpuif_enable(void); +void plat_versal_net_gic_cpuif_disable(void); +void plat_versal_net_gic_pcpu_init(void); +void plat_versal_net_gic_save(void); +void plat_versal_net_gic_resume(void); +void plat_versal_net_gic_redistif_on(void); +void plat_versal_net_gic_redistif_off(void); + +extern uint32_t cpu_clock, platform_id, platform_version; +void board_detection(void); +char *board_name_decode(void); +uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, void *cookie, void *handle, uint64_t flags); +int32_t sip_svc_setup_init(void); +/* + * Register handler to specific GIC entrance + * for INTR_TYPE_EL3 type of interrupt + */ +int request_intr_type_el3(uint32_t irq, interrupt_type_handler_t fiq_handler); + +#endif /* PLAT_PRIVATE_H */ diff --git a/plat/xilinx/versal_net/include/platform_def.h b/plat/xilinx/versal_net/include/platform_def.h new file mode 100644 index 0000000..872b6ee --- /dev/null +++ b/plat/xilinx/versal_net/include/platform_def.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <arch.h> +#include "versal_net_def.h" + +/******************************************************************************* + * Generic platform constants + ******************************************************************************/ + +/* Size of cacheable stacks */ +#define PLATFORM_STACK_SIZE U(0x440) + +#define PLATFORM_CLUSTER_COUNT U(4) +#define PLATFORM_CORE_COUNT_PER_CLUSTER U(4) /* 4 CPUs per cluster */ + +#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * PLATFORM_CORE_COUNT_PER_CLUSTER) + +#define PLAT_MAX_PWR_LVL U(2) +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_OFF_STATE U(2) + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ +/* + * Put BL31 at the top of the Trusted SRAM (just below the shared memory, if + * present). BL31_BASE is calculated using the current BL31 debug size plus a + * little space for growth. + */ +#ifndef VERSAL_NET_ATF_MEM_BASE +# define BL31_BASE U(0xBBF00000) +# define BL31_LIMIT U(0xBC000000) +#else +# define BL31_BASE U(VERSAL_NET_ATF_MEM_BASE) +# define BL31_LIMIT U(VERSAL_NET_ATF_MEM_BASE + VERSAL_NET_ATF_MEM_SIZE) +# ifdef VERSAL_NET_ATF_MEM_PROGBITS_SIZE +# define BL31_PROGBITS_LIMIT U(VERSAL_NET_ATF_MEM_BASE + \ + VERSAL_NET_ATF_MEM_PROGBITS_SIZE) +# endif +#endif + +/******************************************************************************* + * BL32 specific defines. + ******************************************************************************/ +#ifndef VERSAL_NET_BL32_MEM_BASE +# define BL32_BASE U(0x60000000) +# define BL32_LIMIT U(0x80000000) +#else +# define BL32_BASE U(VERSAL_NET_BL32_MEM_BASE) +# define BL32_LIMIT U(VERSAL_NET_BL32_MEM_BASE + VERSAL_NET_BL32_MEM_SIZE) +#endif + +/******************************************************************************* + * BL33 specific defines. + ******************************************************************************/ +#ifndef PRELOADED_BL33_BASE +# define PLAT_ARM_NS_IMAGE_BASE U(0x8000000) +#else +# define PLAT_ARM_NS_IMAGE_BASE U(PRELOADED_BL33_BASE) +#endif + +/******************************************************************************* + * TSP specific defines. + ******************************************************************************/ +#define TSP_SEC_MEM_BASE BL32_BASE +#define TSP_SEC_MEM_SIZE (BL32_LIMIT - BL32_BASE) + +/* ID of the secure physical generic timer interrupt used by the TSP */ +#define TSP_IRQ_SEC_PHY_TIMER ARM_IRQ_SEC_PHY_TIMER + +/******************************************************************************* + * Platform specific page table and MMU setup constants + ******************************************************************************/ +#define PLAT_DDR_LOWMEM_MAX U(0x80000000) + +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32U) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32U) + +#define XILINX_OF_BOARD_DTB_MAX_SIZE U(0x200000) + +#define PLAT_OCM_BASE U(0xBBF00000) +#define PLAT_OCM_LIMIT U(0xBC000000) + +#define IS_TFA_IN_OCM(x) ((x >= PLAT_OCM_BASE) && (x < PLAT_OCM_LIMIT)) + +#ifndef MAX_MMAP_REGIONS +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) +#define MAX_MMAP_REGIONS 9 +#else +#define MAX_MMAP_REGIONS 8 +#endif +#endif + +#ifndef MAX_XLAT_TABLES +#define MAX_XLAT_TABLES U(9) +#endif + +#define CACHE_WRITEBACK_SHIFT U(6) +#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) + +#define PLAT_GICD_BASE_VALUE U(0xE2000000) +#define PLAT_GICR_BASE_VALUE U(0xE2060000) + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLAT_VERSAL_NET_IPI_IRQ 89 +#define PLAT_VERSAL_IPI_IRQ PLAT_VERSAL_NET_IPI_IRQ + +#define PLAT_VERSAL_NET_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(VERSAL_NET_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL) + +#define PLAT_VERSAL_NET_G0_IRQ_PROPS(grp) \ + INTR_PROP_DESC(PLAT_VERSAL_IPI_IRQ, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + +#define IRQ_MAX 200U + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/xilinx/versal_net/include/versal_net_def.h b/plat/xilinx/versal_net/include/versal_net_def.h new file mode 100644 index 0000000..dd20faa --- /dev/null +++ b/plat/xilinx/versal_net/include/versal_net_def.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef VERSAL_NET_DEF_H +#define VERSAL_NET_DEF_H + +#include <plat/arm/common/smccc_def.h> +#include <plat/common/common_def.h> + +#define MAX_INTR_EL3 2 + +/* List all consoles */ +#define VERSAL_NET_CONSOLE_ID_pl011 U(1) +#define VERSAL_NET_CONSOLE_ID_pl011_0 U(1) +#define VERSAL_NET_CONSOLE_ID_pl011_1 U(2) +#define VERSAL_NET_CONSOLE_ID_dcc U(3) + +#define CONSOLE_IS(con) (VERSAL_NET_CONSOLE_ID_ ## con == VERSAL_NET_CONSOLE) + +/* List all platforms */ +#define VERSAL_NET_SILICON U(0) +#define VERSAL_NET_SPP U(1) +#define VERSAL_NET_EMU U(2) +#define VERSAL_NET_QEMU U(3) +#define VERSAL_NET_QEMU_COSIM U(7) + +/* For platform detection */ +#define PMC_TAP U(0xF11A0000) +#define PMC_TAP_VERSION (PMC_TAP + 0x4U) +# define PLATFORM_MASK GENMASK(27U, 24U) +# define PLATFORM_VERSION_MASK GENMASK(31U, 28U) + +/* Global timer reset */ +#define PSX_CRF U(0xEC200000) +#define ACPU0_CLK_CTRL U(0x10C) +#define ACPU_CLK_CTRL_CLKACT BIT(25) + +#define RST_APU0_OFFSET U(0x300) +#define RST_APU_COLD_RESET BIT(0) +#define RST_APU_WARN_RESET BIT(4) +#define RST_APU_CLUSTER_COLD_RESET BIT(8) +#define RST_APU_CLUSTER_WARM_RESET BIT(9) + +#define PSX_CRF_RST_TIMESTAMP_OFFSET U(0x33C) + +#define APU_PCLI (0xECB10000ULL) +#define APU_PCLI_CPU_STEP (0x30ULL) +#define APU_PCLI_CLUSTER_CPU_STEP (4ULL * APU_PCLI_CPU_STEP) +#define APU_PCLI_CLUSTER_OFFSET U(0x8000) +#define APU_PCLI_CLUSTER_STEP U(0x1000) +#define PCLI_PREQ_OFFSET U(0x4) +#define PREQ_CHANGE_REQUEST BIT(0) +#define PCLI_PSTATE_OFFSET U(0x8) +#define PCLI_PSTATE_VAL_SET U(0x48) +#define PCLI_PSTATE_VAL_CLEAR U(0x38) + +/* Firmware Image Package */ +#define VERSAL_NET_PRIMARY_CPU U(0) + +#define CORE_0_ISR_WAKE_OFFSET (0x00000020ULL) +#define APU_PCIL_CORE_X_ISR_WAKE_REG(cpu_id) (APU_PCLI + (CORE_0_ISR_WAKE_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_ISR_WAKE_MASK (0x00000001U) +#define CORE_0_IEN_WAKE_OFFSET (0x00000028ULL) +#define APU_PCIL_CORE_X_IEN_WAKE_REG(cpu_id) (APU_PCLI + (CORE_0_IEN_WAKE_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IEN_WAKE_MASK (0x00000001U) +#define CORE_0_IDS_WAKE_OFFSET (0x0000002CULL) +#define APU_PCIL_CORE_X_IDS_WAKE_REG(cpu_id) (APU_PCLI + (CORE_0_IDS_WAKE_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IDS_WAKE_MASK (0x00000001U) +#define CORE_0_ISR_POWER_OFFSET (0x00000010ULL) +#define APU_PCIL_CORE_X_ISR_POWER_REG(cpu_id) (APU_PCLI + (CORE_0_ISR_POWER_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_ISR_POWER_MASK U(0x00000001) +#define CORE_0_IEN_POWER_OFFSET (0x00000018ULL) +#define APU_PCIL_CORE_X_IEN_POWER_REG(cpu_id) (APU_PCLI + (CORE_0_IEN_POWER_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IEN_POWER_MASK (0x00000001U) +#define CORE_0_IDS_POWER_OFFSET (0x0000001CULL) +#define APU_PCIL_CORE_X_IDS_POWER_REG(cpu_id) (APU_PCLI + (CORE_0_IDS_POWER_OFFSET + \ + (APU_PCLI_CPU_STEP * (cpu_id)))) +#define APU_PCIL_CORE_X_IDS_POWER_MASK (0x00000001U) +#define CORE_PWRDN_EN_BIT_MASK (0x1U) + +/******************************************************************************* + * memory map related constants + ******************************************************************************/ +/* IPP 1.2/SPP 0.9 mapping */ +#define DEVICE0_BASE U(0xE8000000) /* psx, crl, iou */ +#define DEVICE0_SIZE U(0x08000000) +#define DEVICE1_BASE U(0xE2000000) /* gic */ +#define DEVICE1_SIZE U(0x00800000) +#define DEVICE2_BASE U(0xF1000000) /* uart, pmc_tap */ +#define DEVICE2_SIZE U(0x01000000) +#define CRF_BASE U(0xFD1A0000) +#define CRF_SIZE U(0x00600000) +#define IPI_BASE U(0xEB300000) +#define IPI_SIZE U(0x00100000) + +/* CRL */ +#define VERSAL_NET_CRL U(0xEB5E0000) +#define VERSAL_NET_CRL_TIMESTAMP_REF_CTRL_OFFSET U(0x14C) +#define VERSAL_NET_CRL_RST_TIMESTAMP_OFFSET U(0x348) + +#define VERSAL_NET_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT_BIT (1U << 25U) + +/* IOU SCNTRS */ +#define VERSAL_NET_IOU_SCNTRS U(0xEC920000) +#define VERSAL_NET_IOU_SCNTRS_COUNTER_CONTROL_REG_OFFSET U(0) +#define VERSAL_NET_IOU_SCNTRS_BASE_FREQ_OFFSET U(0x20) + +#define VERSAL_NET_IOU_SCNTRS_CONTROL_EN U(1) + +#define APU_CLUSTER0 U(0xECC00000) +#define APU_RVBAR_L_0 U(0x40) +#define APU_RVBAR_H_0 U(0x44) +#define APU_CLUSTER_STEP U(0x100000) + +#define SLCR_OSPI_QSPI_IOU_AXI_MUX_SEL U(0xF1060504) + +/******************************************************************************* + * IRQ constants + ******************************************************************************/ +#define VERSAL_NET_IRQ_SEC_PHY_TIMER U(29) +#define ARM_IRQ_SEC_PHY_TIMER 29 + +/******************************************************************************* + * UART related constants + ******************************************************************************/ +#define VERSAL_NET_UART0_BASE U(0xF1920000) +#define VERSAL_NET_UART1_BASE U(0xF1930000) + +#define UART_BAUDRATE 115200 + +#if CONSOLE_IS(pl011_1) +#define UART_BASE VERSAL_NET_UART1_BASE +#else +/* Default console is UART0 */ +#define UART_BASE VERSAL_NET_UART0_BASE +#endif + +/* Processor core device IDs */ +#define PM_DEV_CLUSTER0_ACPU_0 (0x1810C0AFU) +#define PM_DEV_CLUSTER0_ACPU_1 (0x1810C0B0U) +#define PM_DEV_CLUSTER0_ACPU_2 (0x1810C0B1U) +#define PM_DEV_CLUSTER0_ACPU_3 (0x1810C0B2U) + +#define PM_DEV_CLUSTER1_ACPU_0 (0x1810C0B3U) +#define PM_DEV_CLUSTER1_ACPU_1 (0x1810C0B4U) +#define PM_DEV_CLUSTER1_ACPU_2 (0x1810C0B5U) +#define PM_DEV_CLUSTER1_ACPU_3 (0x1810C0B6U) + +#define PM_DEV_CLUSTER2_ACPU_0 (0x1810C0B7U) +#define PM_DEV_CLUSTER2_ACPU_1 (0x1810C0B8U) +#define PM_DEV_CLUSTER2_ACPU_2 (0x1810C0B9U) +#define PM_DEV_CLUSTER2_ACPU_3 (0x1810C0BAU) + +#define PM_DEV_CLUSTER3_ACPU_0 (0x1810C0BBU) +#define PM_DEV_CLUSTER3_ACPU_1 (0x1810C0BCU) +#define PM_DEV_CLUSTER3_ACPU_2 (0x1810C0BDU) +#define PM_DEV_CLUSTER3_ACPU_3 (0x1810C0BEU) + +#endif /* VERSAL_NET_DEF_H */ diff --git a/plat/xilinx/versal_net/plat_psci.c b/plat/xilinx/versal_net/plat_psci.c new file mode 100644 index 0000000..6e556cd --- /dev/null +++ b/plat/xilinx/versal_net/plat_psci.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <plat_arm.h> + +#include <plat_private.h> +#include <pm_defs.h> + +#define PM_RET_ERROR_NOFEATURE U(19) + +static uintptr_t versal_net_sec_entry; + +static void zynqmp_cpu_standby(plat_local_state_t cpu_state) +{ + dsb(); + wfi(); +} + +static int32_t zynqmp_nopmu_pwr_domain_on(u_register_t mpidr) +{ + uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr); + uint32_t cpu = cpu_id % PLATFORM_CORE_COUNT_PER_CLUSTER; + uint32_t cluster = cpu_id / PLATFORM_CORE_COUNT_PER_CLUSTER; + uintptr_t apu_cluster_base = 0, apu_pcli_base, apu_pcli_cluster = 0; + uintptr_t rst_apu_cluster = PSX_CRF + RST_APU0_OFFSET + (cluster * 0x4); + + VERBOSE("%s: mpidr: 0x%lx, cpuid: %x, cpu: %x, cluster: %x\n", + __func__, mpidr, cpu_id, cpu, cluster); + + if (cpu_id == -1) { + return PSCI_E_INTERN_FAIL; + } + + if (platform_id == VERSAL_NET_SPP && cluster > 1) { + panic(); + } + + if (cluster > 3) { + panic(); + } + + apu_pcli_cluster = APU_PCLI + APU_PCLI_CLUSTER_OFFSET + (cluster * APU_PCLI_CLUSTER_STEP); + apu_cluster_base = APU_CLUSTER0 + (cluster * APU_CLUSTER_STEP); + + /* Enable clock */ + mmio_setbits_32(PSX_CRF + ACPU0_CLK_CTRL + (cluster * 0x4), ACPU_CLK_CTRL_CLKACT); + + /* Enable cluster states */ + mmio_setbits_32(apu_pcli_cluster + PCLI_PSTATE_OFFSET, PCLI_PSTATE_VAL_SET); + mmio_setbits_32(apu_pcli_cluster + PCLI_PREQ_OFFSET, PREQ_CHANGE_REQUEST); + + /* assert core reset */ + mmio_setbits_32(rst_apu_cluster, ((RST_APU_COLD_RESET|RST_APU_WARN_RESET) << cpu)); + + /* program RVBAR */ + mmio_write_32(apu_cluster_base + APU_RVBAR_L_0 + (cpu << 3), + (uint32_t)versal_net_sec_entry); + mmio_write_32(apu_cluster_base + APU_RVBAR_H_0 + (cpu << 3), + versal_net_sec_entry >> 32); + + /* de-assert core reset */ + mmio_clrbits_32(rst_apu_cluster, ((RST_APU_COLD_RESET|RST_APU_WARN_RESET) << cpu)); + + /* clear cluster resets */ + mmio_clrbits_32(rst_apu_cluster, RST_APU_CLUSTER_WARM_RESET); + mmio_clrbits_32(rst_apu_cluster, RST_APU_CLUSTER_COLD_RESET); + + apu_pcli_base = APU_PCLI + (APU_PCLI_CPU_STEP * cpu) + + (APU_PCLI_CLUSTER_CPU_STEP * cluster); + + mmio_write_32(apu_pcli_base + PCLI_PSTATE_OFFSET, PCLI_PSTATE_VAL_CLEAR); + mmio_write_32(apu_pcli_base + PCLI_PREQ_OFFSET, PREQ_CHANGE_REQUEST); + + return PSCI_E_SUCCESS; +} + +static void zynqmp_nopmu_pwr_domain_off(const psci_power_state_t *target_state) +{ +} + +static void __dead2 zynqmp_nopmu_system_reset(void) +{ + while (1) + wfi(); +} + +static int32_t zynqmp_validate_ns_entrypoint(uint64_t ns_entrypoint) +{ + return PSCI_E_SUCCESS; +} + +static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state) +{ +} + +static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + plat_versal_net_gic_pcpu_init(); + plat_versal_net_gic_cpuif_enable(); +} + +static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) +{ +} + +static void __dead2 zynqmp_system_off(void) +{ + while (1) + wfi(); +} + +static int32_t zynqmp_validate_power_state(uint32_t power_state, psci_power_state_t *req_state) +{ + return PSCI_E_SUCCESS; +} + +static void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; + req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; +} + +static const struct plat_psci_ops versal_net_nopmc_psci_ops = { + .cpu_standby = zynqmp_cpu_standby, + .pwr_domain_on = zynqmp_nopmu_pwr_domain_on, + .pwr_domain_off = zynqmp_nopmu_pwr_domain_off, + .system_reset = zynqmp_nopmu_system_reset, + .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint, + .pwr_domain_suspend = zynqmp_pwr_domain_suspend, + .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish, + .pwr_domain_suspend_finish = zynqmp_pwr_domain_suspend_finish, + .system_off = zynqmp_system_off, + .validate_power_state = zynqmp_validate_power_state, + .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state, +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint, + const struct plat_psci_ops **psci_ops) +{ + versal_net_sec_entry = sec_entrypoint; + + VERBOSE("Setting up entry point %lx\n", versal_net_sec_entry); + + *psci_ops = &versal_net_nopmc_psci_ops; + + return 0; +} + +int sip_svc_setup_init(void) +{ + return 0; +} + +static int32_t no_pm_ioctl(uint32_t device_id, uint32_t ioctl_id, + uint32_t arg1, uint32_t arg2) +{ + VERBOSE("%s: ioctl_id: %x, arg1: %x\n", __func__, ioctl_id, arg1); + if (ioctl_id == IOCTL_OSPI_MUX_SELECT) { + mmio_write_32(SLCR_OSPI_QSPI_IOU_AXI_MUX_SEL, arg1); + return 0; + } + return PM_RET_ERROR_NOFEATURE; +} + +static uint64_t no_pm_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, void *cookie, void *handle, uint64_t flags) +{ + int32_t ret; + uint32_t arg[4], api_id; + + arg[0] = (uint32_t)x1; + arg[1] = (uint32_t)(x1 >> 32); + arg[2] = (uint32_t)x2; + arg[3] = (uint32_t)(x2 >> 32); + + api_id = smc_fid & FUNCID_NUM_MASK; + VERBOSE("%s: smc_fid: %x, api_id=0x%x\n", __func__, smc_fid, api_id); + + switch (api_id) { + case PM_IOCTL: + { + ret = no_pm_ioctl(arg[0], arg[1], arg[2], arg[3]); + SMC_RET1(handle, (uint64_t)ret); + } + case PM_GET_CHIPID: + { + uint32_t idcode, version; + + idcode = mmio_read_32(PMC_TAP); + version = mmio_read_32(PMC_TAP_VERSION); + SMC_RET2(handle, ((uint64_t)idcode << 32), version); + } + default: + WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, + void *cookie, void *handle, uint64_t flags) +{ + return no_pm_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); +} diff --git a/plat/xilinx/versal_net/plat_psci_pm.c b/plat/xilinx/versal_net/plat_psci_pm.c new file mode 100644 index 0000000..87e25bc --- /dev/null +++ b/plat/xilinx/versal_net/plat_psci_pm.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <common/debug.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <plat_arm.h> + +#include <plat_private.h> +#include "pm_api_sys.h" +#include "pm_client.h" +#include <pm_common.h> +#include "pm_svc_main.h" +#include "versal_net_def.h" + +static uintptr_t versal_net_sec_entry; + +static int32_t versal_net_pwr_domain_on(u_register_t mpidr) +{ + uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr); + const struct pm_proc *proc; + + VERBOSE("%s: mpidr: 0x%lx, cpuid: %x\n", + __func__, mpidr, cpu_id); + + if (cpu_id == -1) { + return PSCI_E_INTERN_FAIL; + } + + proc = pm_get_proc(cpu_id); + if (!proc) { + return PSCI_E_INTERN_FAIL; + } + + pm_req_wakeup(proc->node_id, (versal_net_sec_entry & 0xFFFFFFFFU) | 0x1U, + versal_net_sec_entry >> 32, 0, 0); + + /* Clear power down request */ + pm_client_wakeup(proc); + + return PSCI_E_SUCCESS; +} + +/** + * versal_net_pwr_domain_off() - This function performs actions to turn off + * core. + * @target_state: Targeted state. + * + */ +static void versal_net_pwr_domain_off(const psci_power_state_t *target_state) +{ + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + + /* Prevent interrupts from spuriously waking up this cpu */ + plat_versal_net_gic_cpuif_disable(); + + /* + * Send request to PMC to power down the appropriate APU CPU + * core. + * According to PSCI specification, CPU_off function does not + * have resume address and CPU core can only be woken up + * invoking CPU_on function, during which resume address will + * be set. + */ + pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0, + SECURE_FLAG); +} + +/** + * versal_net_system_reset() - This function sends the reset request to firmware + * for the system to reset. This function does not + * return. + * + */ +static void __dead2 versal_net_system_reset(void) +{ + /* Send the system reset request to the PMC */ + pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, + pm_get_shutdown_scope(), SECURE_FLAG); + + while (1) { + wfi(); + } +} + +/** + * versal_net_pwr_domain_suspend() - This function sends request to PMC to suspend + * core. + * @target_state: Targeted state. + * + */ +static void versal_net_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + uint32_t state; + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + + plat_versal_net_gic_cpuif_disable(); + + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + plat_versal_net_gic_save(); + } + + state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? + PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; + + /* Send request to PMC to suspend this core */ + pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_net_sec_entry, + SECURE_FLAG); + + /* TODO: disable coherency */ +} + +static void versal_net_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + (void)target_state; + + /* Enable the gic cpu interface */ + plat_versal_net_gic_pcpu_init(); + + /* Program the gic per-cpu distributor or re-distributor interface */ + plat_versal_net_gic_cpuif_enable(); +} + +/** + * versal_net_pwr_domain_suspend_finish() - This function performs actions to finish + * suspend procedure. + * @target_state: Targeted state. + * + */ +static void versal_net_pwr_domain_suspend_finish(const psci_power_state_t *target_state) +{ + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + + /* Clear the APU power control register for this cpu */ + pm_client_wakeup(proc); + + /* TODO: enable coherency */ + + /* APU was turned off, so restore GIC context */ + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + plat_versal_net_gic_resume(); + } + + plat_versal_net_gic_cpuif_enable(); +} + +/** + * versal_net_system_off() - This function sends the system off request + * to firmware. This function does not return. + * + */ +static void __dead2 versal_net_system_off(void) +{ + /* Send the power down request to the PMC */ + pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN, + pm_get_shutdown_scope(), SECURE_FLAG); + + while (1) { + wfi(); + } +} + +/** + * versal_net_validate_power_state() - This function ensures that the power state + * parameter in request is valid. + * @power_state: Power state of core. + * @req_state: Requested state. + * + * Return: Returns status, either PSCI_E_SUCCESS or reason. + * + */ +static int32_t versal_net_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); + + int32_t pstate = psci_get_pstate_type(power_state); + + assert(req_state); + + /* Sanity check the requested state */ + if (pstate == PSTATE_TYPE_STANDBY) { + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; + } else { + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; + } + + /* We expect the 'state id' to be zero */ + if (psci_get_pstate_id(power_state)) { + return PSCI_E_INVALID_PARAMS; + } + + return PSCI_E_SUCCESS; +} + +/** + * versal_net_get_sys_suspend_power_state() - Get power state for system + * suspend. + * @req_state: Requested state. + * + */ +static void versal_net_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + uint64_t i; + + for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) + req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; +} + +static const struct plat_psci_ops versal_net_nopmc_psci_ops = { + .pwr_domain_on = versal_net_pwr_domain_on, + .pwr_domain_off = versal_net_pwr_domain_off, + .pwr_domain_on_finish = versal_net_pwr_domain_on_finish, + .pwr_domain_suspend = versal_net_pwr_domain_suspend, + .pwr_domain_suspend_finish = versal_net_pwr_domain_suspend_finish, + .system_off = versal_net_system_off, + .system_reset = versal_net_system_reset, + .validate_power_state = versal_net_validate_power_state, + .get_sys_suspend_power_state = versal_net_get_sys_suspend_power_state, +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint, + const struct plat_psci_ops **psci_ops) +{ + versal_net_sec_entry = sec_entrypoint; + + VERBOSE("Setting up entry point %lx\n", versal_net_sec_entry); + + *psci_ops = &versal_net_nopmc_psci_ops; + + return 0; +} + +int32_t sip_svc_setup_init(void) +{ + return pm_setup(); +} + +uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, + void *cookie, void *handle, uint64_t flags) +{ + return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); +} diff --git a/plat/xilinx/versal_net/plat_topology.c b/plat/xilinx/versal_net/plat_topology.c new file mode 100644 index 0000000..ee756c4 --- /dev/null +++ b/plat/xilinx/versal_net/plat_topology.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <plat/common/platform.h> + +#include <plat_private.h> +#include <platform_def.h> + +static const uint8_t plat_power_domain_tree_desc[] = { + /* Number of root nodes */ + 1, + /* Number of clusters */ + PLATFORM_CLUSTER_COUNT, + /* Number of children for the first cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, + /* Number of children for the second cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, + /* Number of children for the third cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, + /* Number of children for the fourth cluster node */ + PLATFORM_CORE_COUNT_PER_CLUSTER, +}; + +const uint8_t *plat_get_power_domain_tree_desc(void) +{ + return plat_power_domain_tree_desc; +} + +/******************************************************************************* + * This function implements a part of the critical interface between the psci + * generic layer and the platform that allows the former to query the platform + * to convert an MPIDR to a unique linear index. An error code (-1) is returned + * in case the MPIDR is invalid. + ******************************************************************************/ +int32_t plat_core_pos_by_mpidr(u_register_t mpidr) +{ + uint32_t cluster_id, cpu_id; + + mpidr &= MPIDR_AFFINITY_MASK; + + cluster_id = MPIDR_AFFLVL2_VAL(mpidr); + cpu_id = MPIDR_AFFLVL1_VAL(mpidr); + + if (cluster_id >= PLATFORM_CLUSTER_COUNT) { + return -3; + } + + /* + * Validate cpu_id by checking whether it represents a CPU in + * one of the two clusters present on the platform. + */ + if (cpu_id >= PLATFORM_CORE_COUNT_PER_CLUSTER) { + return -1; + } + + return (cpu_id + (cluster_id * PLATFORM_CORE_COUNT_PER_CLUSTER)); +} diff --git a/plat/xilinx/versal_net/platform.mk b/plat/xilinx/versal_net/platform.mk new file mode 100644 index 0000000..f299189 --- /dev/null +++ b/plat/xilinx/versal_net/platform.mk @@ -0,0 +1,125 @@ +# Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2021-2022, Xilinx, Inc. All rights reserved. +# Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +PLAT_PATH := plat/xilinx/versal_net + +# A78 Erratum for SoC +ERRATA_A78_AE_1941500 := 1 +ERRATA_A78_AE_1951502 := 1 +ERRATA_A78_AE_2376748 := 1 +ERRATA_A78_AE_2395408 := 1 + +override PROGRAMMABLE_RESET_ADDRESS := 1 +PSCI_EXTENDED_STATE_ID := 1 +SEPARATE_CODE_AND_RODATA := 1 +override RESET_TO_BL31 := 1 +PL011_GENERIC_UART := 1 +IPI_CRC_CHECK := 0 +GIC_ENABLE_V4_EXTN := 0 +GICV3_SUPPORT_GIC600 := 1 +TFA_NO_PM := 0 + +override CTX_INCLUDE_AARCH32_REGS := 0 + +ifdef TFA_NO_PM + $(eval $(call add_define,TFA_NO_PM)) +endif + +ifdef VERSAL_NET_ATF_MEM_BASE + $(eval $(call add_define,VERSAL_NET_ATF_MEM_BASE)) + + ifndef VERSAL_NET_ATF_MEM_SIZE + $(error "VERSAL_NET_ATF_BASE defined without VERSAL_NET_ATF_SIZE") + endif + $(eval $(call add_define,VERSAL_NET_ATF_MEM_SIZE)) + + ifdef VERSAL_NET_ATF_MEM_PROGBITS_SIZE + $(eval $(call add_define,VERSAL_NET_ATF_MEM_PROGBITS_SIZE)) + endif +endif + +ifdef VERSAL_NET_BL32_MEM_BASE + $(eval $(call add_define,VERSAL_NET_BL32_MEM_BASE)) + + ifndef VERSAL_NET_BL32_MEM_SIZE + $(error "VERSAL_NET_BL32_BASE defined without VERSAL_NET_BL32_SIZE") + endif + $(eval $(call add_define,VERSAL_NET_BL32_MEM_SIZE)) +endif + +ifdef IPI_CRC_CHECK + $(eval $(call add_define,IPI_CRC_CHECK)) +endif + +USE_COHERENT_MEM := 0 +HW_ASSISTED_COHERENCY := 1 + +VERSAL_NET_CONSOLE ?= pl011 +ifeq (${VERSAL_NET_CONSOLE}, $(filter ${VERSAL_NET_CONSOLE},pl011 pl011_0 pl011_1 dcc)) +else + $(error Please define VERSAL_NET_CONSOLE) +endif + +$(eval $(call add_define_val,VERSAL_NET_CONSOLE,VERSAL_NET_CONSOLE_ID_${VERSAL_NET_CONSOLE})) + +ifdef XILINX_OF_BOARD_DTB_ADDR +$(eval $(call add_define,XILINX_OF_BOARD_DTB_ADDR)) +endif + +# enable assert() for release/debug builds +ENABLE_ASSERTIONS := 1 + +PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ + -Iplat/xilinx/common/include/ \ + -Iplat/xilinx/common/ipi_mailbox_service/ \ + -I${PLAT_PATH}/include/ \ + -Iplat/xilinx/versal/pm_service/ + +# Include GICv3 driver files +include drivers/arm/gic/v3/gicv3.mk +include lib/xlat_tables_v2/xlat_tables.mk +include lib/libfdt/libfdt.mk + +PLAT_BL_COMMON_SOURCES := \ + drivers/arm/dcc/dcc_console.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + ${GICV3_SOURCES} \ + drivers/arm/pl011/aarch64/pl011_console.S \ + plat/common/aarch64/crash_console_helpers.S \ + plat/arm/common/arm_common.c \ + plat/common/plat_gicv3.c \ + ${PLAT_PATH}/aarch64/versal_net_helpers.S \ + ${PLAT_PATH}/aarch64/versal_net_common.c + +BL31_SOURCES += drivers/arm/cci/cci.c \ + lib/cpus/aarch64/cortex_a78_ae.S \ + lib/cpus/aarch64/cortex_a78.S \ + plat/common/plat_psci_common.c +ifeq ($(TFA_NO_PM), 0) +BL31_SOURCES += plat/xilinx/common/pm_service/pm_api_sys.c \ + plat/xilinx/common/pm_service/pm_ipi.c \ + ${PLAT_PATH}/plat_psci_pm.c \ + plat/xilinx/common/pm_service/pm_svc_main.c \ + ${PLAT_PATH}/pm_service/pm_client.c \ + ${PLAT_PATH}/versal_net_ipi.c +else +BL31_SOURCES += ${PLAT_PATH}/plat_psci.c +endif +BL31_SOURCES += plat/xilinx/common/plat_fdt.c \ + plat/xilinx/common/plat_startup.c \ + plat/xilinx/common/plat_console.c \ + plat/xilinx/common/ipi.c \ + plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ + plat/xilinx/common/versal.c \ + ${PLAT_PATH}/bl31_versal_net_setup.c \ + ${PLAT_PATH}/plat_topology.c \ + common/fdt_fixup.c \ + common/fdt_wrappers.c \ + ${LIBFDT_SRCS} \ + ${PLAT_PATH}/sip_svc_setup.c \ + ${PLAT_PATH}/versal_net_gicv3.c \ + ${XLAT_TABLES_LIB_SRCS} diff --git a/plat/xilinx/versal_net/pm_service/pm_client.c b/plat/xilinx/versal_net/pm_service/pm_client.c new file mode 100644 index 0000000..cff400c --- /dev/null +++ b/plat/xilinx/versal_net/pm_service/pm_client.c @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * APU specific definition of processors in the subsystem as well as functions + * for getting information about and changing state of the APU. + */ + +#include <assert.h> + +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv3.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> +#include <lib/utils.h> +#include <plat/common/platform.h> + +#include <plat_ipi.h> +#include <platform_def.h> +#include "pm_api_sys.h" +#include "pm_client.h" +#include <versal_net_def.h> + +#define UNDEFINED_CPUID (~0) + +DEFINE_RENAME_SYSREG_RW_FUNCS(cpu_pwrctrl_val, S3_0_C15_C2_7) + +/* + * 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_client_secure_lock); +static inline void pm_client_lock_get(void) +{ + bakery_lock_get(&pm_client_secure_lock); +} + +static inline void pm_client_lock_release(void) +{ + bakery_lock_release(&pm_client_secure_lock); +} +#else +spinlock_t pm_client_secure_lock; +static inline void pm_client_lock_get(void) +{ + spin_lock(&pm_client_secure_lock); +} + +static inline void pm_client_lock_release(void) +{ + spin_unlock(&pm_client_secure_lock); +} +#endif + +static const struct pm_ipi apu_ipi = { + .local_ipi_id = IPI_LOCAL_ID, + .remote_ipi_id = IPI_REMOTE_ID, + .buffer_base = IPI_BUFFER_LOCAL_BASE, +}; + +/* Order in pm_procs_all array must match cpu ids */ +static const struct pm_proc pm_procs_all[] = { + { + .node_id = PM_DEV_CLUSTER0_ACPU_0, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER0_ACPU_1, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER0_ACPU_2, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER0_ACPU_3, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER1_ACPU_0, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER1_ACPU_1, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER1_ACPU_2, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER1_ACPU_3, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER2_ACPU_0, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER2_ACPU_1, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER2_ACPU_2, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER2_ACPU_3, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER3_ACPU_0, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER3_ACPU_1, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER3_ACPU_2, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + }, + { + .node_id = PM_DEV_CLUSTER3_ACPU_3, + .ipi = &apu_ipi, + .pwrdn_mask = 0, + } +}; + +const struct pm_proc *primary_proc = &pm_procs_all[0]; + +/** + * pm_get_proc() - returns pointer to the proc structure. + * @cpuid: id of the cpu whose proc struct pointer should be returned. + * + * Return: Pointer to a proc structure if proc is found, otherwise NULL. + * + */ +const struct pm_proc *pm_get_proc(uint32_t cpuid) +{ + if (cpuid < ARRAY_SIZE(pm_procs_all)) { + return &pm_procs_all[cpuid]; + } + + NOTICE("ERROR: cpuid: %d proc NULL\n", cpuid); + return NULL; +} + +/** + * irq_to_pm_node_idx - Get PM node index corresponding to the interrupt number. + * @irq: Interrupt number. + * + * Return: PM node index corresponding to the specified interrupt. + * + */ +enum pm_device_node_idx irq_to_pm_node_idx(uint32_t irq) +{ + enum pm_device_node_idx dev_idx = XPM_NODEIDX_DEV_MIN; + + assert(irq <= IRQ_MAX); + + switch (irq) { + case 20: + dev_idx = XPM_NODEIDX_DEV_GPIO; + break; + case 21: + dev_idx = XPM_NODEIDX_DEV_I2C_0; + break; + case 22: + dev_idx = XPM_NODEIDX_DEV_I2C_1; + break; + case 23: + dev_idx = XPM_NODEIDX_DEV_SPI_0; + break; + case 24: + dev_idx = XPM_NODEIDX_DEV_SPI_1; + break; + case 25: + dev_idx = XPM_NODEIDX_DEV_UART_0; + break; + case 26: + dev_idx = XPM_NODEIDX_DEV_UART_1; + break; + case 27: + dev_idx = XPM_NODEIDX_DEV_CAN_FD_0; + break; + case 28: + dev_idx = XPM_NODEIDX_DEV_CAN_FD_1; + break; + case 29: + case 30: + case 31: + case 32: + case 33: + case 98: + dev_idx = XPM_NODEIDX_DEV_USB_0; + break; + case 34: + case 35: + case 36: + case 37: + case 38: + case 99: + dev_idx = XPM_NODEIDX_DEV_USB_1; + break; + case 39: + case 40: + dev_idx = XPM_NODEIDX_DEV_GEM_0; + break; + case 41: + case 42: + dev_idx = XPM_NODEIDX_DEV_GEM_1; + break; + case 43: + case 44: + case 45: + dev_idx = XPM_NODEIDX_DEV_TTC_0; + break; + case 46: + case 47: + case 48: + dev_idx = XPM_NODEIDX_DEV_TTC_1; + break; + case 49: + case 50: + case 51: + dev_idx = XPM_NODEIDX_DEV_TTC_2; + break; + case 52: + case 53: + case 54: + dev_idx = XPM_NODEIDX_DEV_TTC_3; + break; + case 72: + dev_idx = XPM_NODEIDX_DEV_ADMA_0; + break; + case 73: + dev_idx = XPM_NODEIDX_DEV_ADMA_1; + break; + case 74: + dev_idx = XPM_NODEIDX_DEV_ADMA_2; + break; + case 75: + dev_idx = XPM_NODEIDX_DEV_ADMA_3; + break; + case 76: + dev_idx = XPM_NODEIDX_DEV_ADMA_4; + break; + case 77: + dev_idx = XPM_NODEIDX_DEV_ADMA_5; + break; + case 78: + dev_idx = XPM_NODEIDX_DEV_ADMA_6; + break; + case 79: + dev_idx = XPM_NODEIDX_DEV_ADMA_7; + break; + case 184: + case 185: + dev_idx = XPM_NODEIDX_DEV_SDIO_0; + break; + case 186: + case 187: + dev_idx = XPM_NODEIDX_DEV_SDIO_1; + break; + case 200: + dev_idx = XPM_NODEIDX_DEV_RTC; + break; + default: + dev_idx = XPM_NODEIDX_DEV_MIN; + break; + } + + return dev_idx; +} + +/** + * pm_client_suspend() - Client-specific suspend actions. This function + * should contain any PU-specific actions required + * prior to sending suspend request to PMU. Actions + * taken depend on the state system is suspending to. + * @proc: processor which need to suspend. + * @state: desired suspend state. + * + */ +void pm_client_suspend(const struct pm_proc *proc, uint32_t state) +{ + uint32_t cpu_id = plat_my_core_pos(); + uintptr_t val; + + pm_client_lock_get(); + + if (state == PM_STATE_SUSPEND_TO_RAM) { + pm_client_set_wakeup_sources((uint32_t)proc->node_id); + } + + val = read_cpu_pwrctrl_val(); + val |= CORE_PWRDN_EN_BIT_MASK; + write_cpu_pwrctrl_val(val); + + isb(); + + /* Enable power down interrupt */ + mmio_write_32(APU_PCIL_CORE_X_IEN_POWER_REG(cpu_id), + APU_PCIL_CORE_X_IEN_POWER_MASK); + /* Enable wake interrupt */ + mmio_write_32(APU_PCIL_CORE_X_IEN_WAKE_REG(cpu_id), + APU_PCIL_CORE_X_IEN_WAKE_MASK); + + pm_client_lock_release(); +} + +/** + * pm_get_cpuid() - get the local cpu ID for a global node ID. + * @nid: node id of the processor. + * + * Return: the cpu ID (starting from 0) for the subsystem. + * + */ +static uint32_t pm_get_cpuid(uint32_t nid) +{ + for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { + if (pm_procs_all[i].node_id == nid) { + return i; + } + } + return UNDEFINED_CPUID; +} + +/** + * pm_client_wakeup() - Client-specific wakeup actions. + * @proc: Processor which need to wakeup. + * + * This function should contain any PU-specific actions + * required for waking up another APU core. + * + */ +void pm_client_wakeup(const struct pm_proc *proc) +{ + uint32_t cpuid = pm_get_cpuid(proc->node_id); + uintptr_t val; + + if (cpuid == UNDEFINED_CPUID) { + return; + } + + pm_client_lock_get(); + + /* Clear powerdown request */ + val = read_cpu_pwrctrl_val(); + val &= ~CORE_PWRDN_EN_BIT_MASK; + write_cpu_pwrctrl_val(val); + + isb(); + + /* Disabled power down interrupt */ + mmio_write_32(APU_PCIL_CORE_X_IDS_POWER_REG(cpuid), + APU_PCIL_CORE_X_IDS_POWER_MASK); + /* Disable wake interrupt */ + mmio_write_32(APU_PCIL_CORE_X_IDS_WAKE_REG(cpuid), + APU_PCIL_CORE_X_IDS_WAKE_MASK); + + pm_client_lock_release(); +} + +/** + * pm_client_abort_suspend() - Client-specific abort-suspend actions. + * + * This function should contain any PU-specific actions + * required for aborting a prior suspend request. + * + */ +void pm_client_abort_suspend(void) +{ + uint32_t cpu_id = plat_my_core_pos(); + uintptr_t val; + + /* Enable interrupts at processor level (for current cpu) */ + gicv3_cpuif_enable(plat_my_core_pos()); + + pm_client_lock_get(); + + /* Clear powerdown request */ + val = read_cpu_pwrctrl_val(); + val &= ~CORE_PWRDN_EN_BIT_MASK; + write_cpu_pwrctrl_val(val); + + isb(); + + /* Disabled power down interrupt */ + mmio_write_32(APU_PCIL_CORE_X_IDS_POWER_REG(cpu_id), + APU_PCIL_CORE_X_IDS_POWER_MASK); + + pm_client_lock_release(); +} diff --git a/plat/xilinx/versal_net/sip_svc_setup.c b/plat/xilinx/versal_net/sip_svc_setup.c new file mode 100644 index 0000000..0c27dec --- /dev/null +++ b/plat/xilinx/versal_net/sip_svc_setup.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Top level SMC handler for SiP calls. Dispatch PM calls to PM SMC handler. */ + +#include <errno.h> +#include <inttypes.h> + +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <tools_share/uuid.h> + +#include "ipi_mailbox_svc.h" +#include "plat_private.h" +#include "pm_svc_main.h" + +/* SMC function IDs for SiP Service queries */ +#define VERSAL_NET_SIP_SVC_CALL_COUNT (0x8200ff00U) +#define VERSAL_NET_SIP_SVC_UID (0x8200ff01U) +#define VERSAL_NET_SIP_SVC_VERSION (0x8200ff03U) + +/* SiP Service Calls version numbers */ +#define SIP_SVC_VERSION_MAJOR (0U) +#define SIP_SVC_VERSION_MINOR (1U) + +/* These macros are used to identify PM calls from the SMC function ID */ +#define SIP_FID_MASK GENMASK(23, 16) +#define XLNX_FID_MASK GENMASK(23, 12) +#define PM_FID_VALUE 0u +#define IPI_FID_VALUE 0x1000u +#define is_pm_fid(_fid) (((_fid) & XLNX_FID_MASK) == PM_FID_VALUE) +#define is_ipi_fid(_fid) (((_fid) & XLNX_FID_MASK) == IPI_FID_VALUE) + +/* SiP Service UUID */ +DEFINE_SVC_UUID2(versal_net_sip_uuid, + 0x80d4c25a, 0xebaf, 0x11eb, 0x94, 0x68, + 0x0b, 0x4e, 0x3b, 0x8f, 0xc3, 0x60); + +/** + * sip_svc_setup() - Setup SiP Service + * + * Return: 0 on success, negative error code on failure. + * + */ +static int32_t sip_svc_setup(void) +{ + return sip_svc_setup_init(); +} + +/* + * sip_svc_smc_handler() - Top-level SiP Service SMC handler + * + * Handler for all SiP SMC calls. Handles standard SIP requests + * and calls PM SMC handler if the call is for a PM-API function. + */ +static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + VERBOSE("SMCID: 0x%08x, x1: 0x%016" PRIx64 ", x2: 0x%016" PRIx64 ", x3: 0x%016" PRIx64 ", x4: 0x%016" PRIx64 "\n", + smc_fid, x1, x2, x3, x4); + + if (smc_fid & SIP_FID_MASK) { + WARN("SMC out of SiP assinged range: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } + + /* Let PM SMC handler deal with PM-related requests */ + if (is_pm_fid(smc_fid)) { + return smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } + + /* Let IPI SMC handler deal with IPI-related requests if platform */ + if (is_ipi_fid(smc_fid)) { + return ipi_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); + } + + /* Let PM SMC handler deal with PM-related requests */ + switch (smc_fid) { + case VERSAL_NET_SIP_SVC_CALL_COUNT: + /* PM functions + default functions */ + SMC_RET1(handle, 2); + + case VERSAL_NET_SIP_SVC_UID: + SMC_UUID_RET(handle, versal_net_sip_uuid); + + case VERSAL_NET_SIP_SVC_VERSION: + SMC_RET2(handle, SIP_SVC_VERSION_MAJOR, SIP_SVC_VERSION_MINOR); + + default: + WARN("Unimplemented SiP Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +/* Register PM Service Calls as runtime service */ +DECLARE_RT_SVC( + sip_svc, + OEN_SIP_START, + OEN_SIP_END, + SMC_TYPE_FAST, + sip_svc_setup, + sip_svc_smc_handler); diff --git a/plat/xilinx/versal_net/tsp/tsp-versal_net.mk b/plat/xilinx/versal_net/tsp/tsp-versal_net.mk new file mode 100644 index 0000000..87638ab --- /dev/null +++ b/plat/xilinx/versal_net/tsp/tsp-versal_net.mk @@ -0,0 +1,13 @@ +# +# Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# TSP source files specific to Versal NET platform + +PLAT_XILINX_COMMON := plat/xilinx/common/ + +include ${PLAT_XILINX_COMMON}/tsp/tsp.mk + +BL32_SOURCES += plat/xilinx/versal_net/plat_topology.c \ + ${XLAT_TABLES_LIB_SRCS} diff --git a/plat/xilinx/versal_net/versal_net_gicv3.c b/plat/xilinx/versal_net/versal_net_gicv3.c new file mode 100644 index 0000000..2fdef12 --- /dev/null +++ b/plat/xilinx/versal_net/versal_net_gicv3.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> +#include <common/interrupt_props.h> +#include <drivers/arm/gicv3.h> +#include <lib/utils.h> +#include <plat/common/platform.h> + +#include <plat_private.h> +#include <platform_def.h> + +/****************************************************************************** + * The following functions are defined as weak to allow a platform to override + * the way the GICv3 driver is initialised and used. + *****************************************************************************/ +#pragma weak plat_versal_net_gic_driver_init +#pragma weak plat_versal_net_gic_init +#pragma weak plat_versal_net_gic_cpuif_enable +#pragma weak plat_versal_net_gic_cpuif_disable +#pragma weak plat_versal_net_gic_pcpu_init +#pragma weak plat_versal_net_gic_redistif_on +#pragma weak plat_versal_net_gic_redistif_off + +/* The GICv3 driver only needs to be initialized in EL3 */ +static uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT]; + +static const interrupt_prop_t versal_net_interrupt_props[] = { + PLAT_VERSAL_NET_G1S_IRQ_PROPS(INTR_GROUP1S), + PLAT_VERSAL_NET_G0_IRQ_PROPS(INTR_GROUP0) +}; + +/* + * We save and restore the GICv3 context on system suspend. Allocate the + * data in the designated EL3 Secure carve-out memory. + */ +static gicv3_redist_ctx_t rdist_ctx __section(".versal_net_el3_tzc_dram"); +static gicv3_dist_ctx_t dist_ctx __section(".versal_net_el3_tzc_dram"); + +/* + * MPIDR hashing function for translating MPIDRs read from GICR_TYPER register + * to core position. + * + * Calculating core position is dependent on MPIDR_EL1.MT bit. However, affinity + * values read from GICR_TYPER don't have an MT field. To reuse the same + * translation used for CPUs, we insert MT bit read from the PE's MPIDR into + * that read from GICR_TYPER. + * + * Assumptions: + * + * - All CPUs implemented in the system have MPIDR_EL1.MT bit set; + * - No CPUs implemented in the system use affinity level 3. + */ +static uint32_t versal_net_gicv3_mpidr_hash(u_register_t mpidr) +{ + mpidr |= (read_mpidr_el1() & MPIDR_MT_MASK); + return plat_core_pos_by_mpidr(mpidr); +} + +static const gicv3_driver_data_t versal_net_gic_data __unused = { + .gicd_base = PLAT_GICD_BASE_VALUE, + .gicr_base = PLAT_GICR_BASE_VALUE, + .interrupt_props = versal_net_interrupt_props, + .interrupt_props_num = ARRAY_SIZE(versal_net_interrupt_props), + .rdistif_num = PLATFORM_CORE_COUNT, + .rdistif_base_addrs = rdistif_base_addrs, + .mpidr_to_core_pos = versal_net_gicv3_mpidr_hash +}; + +void __init plat_versal_net_gic_driver_init(void) +{ + /* + * The GICv3 driver is initialized in EL3 and does not need + * to be initialized again in SEL1. This is because the S-EL1 + * can use GIC system registers to manage interrupts and does + * not need GIC interface base addresses to be configured. + */ +#if IMAGE_BL31 + gicv3_driver_init(&versal_net_gic_data); +#endif +} + +/****************************************************************************** + * Versal NET common helper to initialize the GIC. Only invoked by BL31 + *****************************************************************************/ +void __init plat_versal_net_gic_init(void) +{ + gicv3_distif_init(); + gicv3_rdistif_init(plat_my_core_pos()); + gicv3_cpuif_enable(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal NET common helper to enable the GIC CPU interface + *****************************************************************************/ +void plat_versal_net_gic_cpuif_enable(void) +{ + gicv3_cpuif_enable(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal NET common helper to disable the GIC CPU interface + *****************************************************************************/ +void plat_versal_net_gic_cpuif_disable(void) +{ + gicv3_cpuif_disable(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal NET common helper to initialize the per-cpu redistributor interface in + * GICv3 + *****************************************************************************/ +void plat_versal_net_gic_pcpu_init(void) +{ + gicv3_rdistif_init(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal NET common helpers to power GIC redistributor interface + *****************************************************************************/ +void plat_versal_net_gic_redistif_on(void) +{ + gicv3_rdistif_on(plat_my_core_pos()); +} + +void plat_versal_net_gic_redistif_off(void) +{ + gicv3_rdistif_off(plat_my_core_pos()); +} + +/****************************************************************************** + * Versal NET common helper to save & restore the GICv3 on resume from system + * suspend + *****************************************************************************/ +void plat_versal_net_gic_save(void) +{ + /* + * If an ITS is available, save its context before + * the Redistributor using: + * gicv3_its_save_disable(gits_base, &its_ctx[i]) + * Additionnaly, an implementation-defined sequence may + * be required to save the whole ITS state. + */ + + /* + * Save the GIC Redistributors and ITS contexts before the + * Distributor context. As we only handle SYSTEM SUSPEND API, + * we only need to save the context of the CPU that is issuing + * the SYSTEM SUSPEND call, i.e. the current CPU. + */ + gicv3_rdistif_save(plat_my_core_pos(), &rdist_ctx); + + /* Save the GIC Distributor context */ + gicv3_distif_save(&dist_ctx); + + /* + * From here, all the components of the GIC can be safely powered down + * as long as there is an alternate way to handle wakeup interrupt + * sources. + */ +} + +void plat_versal_net_gic_resume(void) +{ + /* Restore the GIC Distributor context */ + gicv3_distif_init_restore(&dist_ctx); + + /* + * Restore the GIC Redistributor and ITS contexts after the + * Distributor context. As we only handle SYSTEM SUSPEND API, + * we only need to restore the context of the CPU that issued + * the SYSTEM SUSPEND call. + */ + gicv3_rdistif_init_restore(plat_my_core_pos(), &rdist_ctx); + + /* + * If an ITS is available, restore its context after + * the Redistributor using: + * gicv3_its_restore(gits_base, &its_ctx[i]) + * An implementation-defined sequence may be required to + * restore the whole ITS state. The ITS must also be + * re-enabled after this sequence has been executed. + */ +} diff --git a/plat/xilinx/versal_net/versal_net_ipi.c b/plat/xilinx/versal_net/versal_net_ipi.c new file mode 100644 index 0000000..e8d8fb7 --- /dev/null +++ b/plat/xilinx/versal_net/versal_net_ipi.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Versal NET IPI agent registers access management + */ + +#include <lib/utils_def.h> +#include <ipi.h> +#include <plat_ipi.h> + +/* versal_net ipi configuration table */ +static const struct ipi_config versal_net_ipi_table[IPI_ID_MAX] = { + /* A72 IPI */ + [IPI_ID_APU] = { + .ipi_bit_mask = IPI0_TRIG_BIT, + .ipi_reg_base = IPI0_REG_BASE, + .secure_only = 0, + }, + + /* PMC IPI */ + [IPI_ID_PMC] = { + .ipi_bit_mask = PMC_IPI_TRIG_BIT, + .ipi_reg_base = IPI0_REG_BASE, + .secure_only = IPI_SECURE_MASK, + }, + + /* RPU0 IPI */ + [IPI_ID_RPU0] = { + .ipi_bit_mask = IPI1_TRIG_BIT, + .ipi_reg_base = IPI1_REG_BASE, + .secure_only = 0, + }, + + /* RPU1 IPI */ + [IPI_ID_RPU1] = { + .ipi_bit_mask = IPI2_TRIG_BIT, + .ipi_reg_base = IPI2_REG_BASE, + .secure_only = 0, + }, + + /* IPI3 IPI */ + [IPI_ID_3] = { + .ipi_bit_mask = IPI3_TRIG_BIT, + .ipi_reg_base = IPI3_REG_BASE, + .secure_only = 0, + }, + + /* IPI4 IPI */ + [IPI_ID_4] = { + .ipi_bit_mask = IPI4_TRIG_BIT, + .ipi_reg_base = IPI4_REG_BASE, + .secure_only = 0, + }, + + /* IPI5 IPI */ + [IPI_ID_5] = { + .ipi_bit_mask = IPI5_TRIG_BIT, + .ipi_reg_base = IPI5_REG_BASE, + .secure_only = 0, + }, +}; + +/* versal_net_ipi_config_table_init() - Initialize versal_net IPI configuration + * data. + * @ipi_config_table: IPI configuration table. + * @ipi_total: Total number of IPI available. + * + */ +void versal_net_ipi_config_table_init(void) +{ + ipi_config_table_init(versal_net_ipi_table, ARRAY_SIZE(versal_net_ipi_table)); +} diff --git a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c new file mode 100644 index 0000000..dba1734 --- /dev/null +++ b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdbool.h> +#include <string.h> + +#include <common/debug.h> +#include <drivers/generic_delay_timer.h> +#include <lib/mmio.h> +#include <lib/smccc.h> +#include <lib/xlat_tables/xlat_tables.h> +#include <plat/common/platform.h> +#include <services/arm_arch_svc.h> + +#include <plat_ipi.h> +#include <plat_private.h> +#include <plat_startup.h> + +#include "zynqmp_pm_api_sys.h" + +/* + * Table of regions to map using the MMU. + * This doesn't include TZRAM as the 'mem_layout' argument passed to + * configure_mmu_elx() will give the available subset of that, + */ +const mmap_region_t plat_zynqmp_mmap[] = { + { DEVICE0_BASE, DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE }, + { DEVICE1_BASE, DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE }, + { CRF_APB_BASE, CRF_APB_BASE, CRF_APB_SIZE, MT_DEVICE | MT_RW | MT_SECURE }, + {0} +}; + +const mmap_region_t *plat_get_mmap(void) +{ + return plat_zynqmp_mmap; +} + +static uint32_t zynqmp_get_silicon_ver(void) +{ + static unsigned int ver; + + if (!ver) { + ver = mmio_read_32(ZYNQMP_CSU_BASEADDR + + ZYNQMP_CSU_VERSION_OFFSET); + ver &= ZYNQMP_SILICON_VER_MASK; + ver >>= ZYNQMP_SILICON_VER_SHIFT; + } + + return ver; +} + +uint32_t get_uart_clk(void) +{ + unsigned int ver = zynqmp_get_silicon_ver(); + + if (ver == ZYNQMP_CSU_VERSION_QEMU) { + return 133000000; + } else { + return 100000000; + } +} + +#if LOG_LEVEL >= LOG_LEVEL_NOTICE +static const struct { + uint8_t id; + bool evexists; + uint16_t ver; + char *name; +} __packed zynqmp_devices[] = { + { + .id = 0x10, + .name = "XCZU3EG", + }, + { + .id = 0x10, + .ver = 0x2c, + .name = "XCZU3CG", + }, + { + .id = 0x11, + .name = "XCZU2EG", + }, + { + .id = 0x11, + .ver = 0x2c, + .name = "XCZU2CG", + }, + { + .id = 0x20, + .name = "XCZU5EV", + .evexists = true, + }, + { + .id = 0x20, + .ver = 0x100, + .name = "XCZU5EG", + .evexists = true, + }, + { + .id = 0x20, + .ver = 0x12c, + .name = "XCZU5CG", + }, + { + .id = 0x21, + .name = "XCZU4EV", + .evexists = true, + }, + { + .id = 0x21, + .ver = 0x100, + .name = "XCZU4EG", + .evexists = true, + }, + { + .id = 0x21, + .ver = 0x12c, + .name = "XCZU4CG", + }, + { + .id = 0x30, + .name = "XCZU7EV", + .evexists = true, + }, + { + .id = 0x30, + .ver = 0x100, + .name = "XCZU7EG", + .evexists = true, + }, + { + .id = 0x30, + .ver = 0x12c, + .name = "XCZU7CG", + }, + { + .id = 0x38, + .name = "XCZU9EG", + }, + { + .id = 0x38, + .ver = 0x2c, + .name = "XCZU9CG", + }, + { + .id = 0x39, + .name = "XCZU6EG", + }, + { + .id = 0x39, + .ver = 0x2c, + .name = "XCZU6CG", + }, + { + .id = 0x40, + .name = "XCZU11EG", + }, + { + .id = 0x50, + .name = "XCZU15EG", + }, + { + .id = 0x58, + .name = "XCZU19EG", + }, + { + .id = 0x59, + .name = "XCZU17EG", + }, + { + .id = 0x60, + .name = "XCZU28DR", + }, + { + .id = 0x61, + .name = "XCZU21DR", + }, + { + .id = 0x62, + .name = "XCZU29DR", + }, + { + .id = 0x63, + .name = "XCZU23DR", + }, + { + .id = 0x64, + .name = "XCZU27DR", + }, + { + .id = 0x65, + .name = "XCZU25DR", + }, + { + .id = 0x66, + .name = "XCZU39DR", + }, + { + .id = 0x7d, + .name = "XCZU43DR", + }, + { + .id = 0x78, + .name = "XCZU46DR", + }, + { + .id = 0x7f, + .name = "XCZU47DR", + }, + { + .id = 0x7b, + .name = "XCZU48DR", + }, + { + .id = 0x7e, + .name = "XCZU49DR", + }, +}; + +#define ZYNQMP_PL_STATUS_BIT 9 +#define ZYNQMP_PL_STATUS_MASK BIT(ZYNQMP_PL_STATUS_BIT) +#define ZYNQMP_CSU_VERSION_MASK ~(ZYNQMP_PL_STATUS_MASK) + +#define SILICON_ID_XCK24 0x4712093U +#define SILICON_ID_XCK26 0x4724093U + +static char *zynqmp_get_silicon_idcode_name(void) +{ + uint32_t id, ver, chipid[2]; + size_t i, j, len; + const char *name = "EG/EV"; + + if (pm_get_chipid(chipid) != PM_RET_SUCCESS) { + return "XCZUUNKN"; + } + + id = chipid[0] & (ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | + ZYNQMP_CSU_IDCODE_SVD_MASK); + id >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; + ver = chipid[1] >> ZYNQMP_EFUSE_IPDISABLE_SHIFT; + + for (i = 0; i < ARRAY_SIZE(zynqmp_devices); i++) { + if (zynqmp_devices[i].id == id && + zynqmp_devices[i].ver == (ver & ZYNQMP_CSU_VERSION_MASK)) { + break; + } + } + + if (i >= ARRAY_SIZE(zynqmp_devices)) { + switch (chipid[0]) { + case SILICON_ID_XCK24: + return "XCK24"; + case SILICON_ID_XCK26: + return "XCK26"; + default: + return "XCZUUNKN"; + } + } + + if (!zynqmp_devices[i].evexists) { + return zynqmp_devices[i].name; + } + + if ((ver & ZYNQMP_PL_STATUS_MASK) != 0U) { + return zynqmp_devices[i].name; + } + + len = strlen(zynqmp_devices[i].name) - 2; + for (j = 0; j < strlen(name); j++) { + zynqmp_devices[i].name[len] = name[j]; + len++; + } + zynqmp_devices[i].name[len] = '\0'; + + return zynqmp_devices[i].name; +} + +static unsigned int zynqmp_get_rtl_ver(void) +{ + uint32_t ver; + + ver = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_VERSION_OFFSET); + ver &= ZYNQMP_RTL_VER_MASK; + ver >>= ZYNQMP_RTL_VER_SHIFT; + + return ver; +} + +static char *zynqmp_print_silicon_idcode(void) +{ + uint32_t id, maskid, tmp; + + id = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET); + + tmp = id; + tmp &= ZYNQMP_CSU_IDCODE_XILINX_ID_MASK | + ZYNQMP_CSU_IDCODE_FAMILY_MASK; + maskid = ZYNQMP_CSU_IDCODE_XILINX_ID << ZYNQMP_CSU_IDCODE_XILINX_ID_SHIFT | + ZYNQMP_CSU_IDCODE_FAMILY << ZYNQMP_CSU_IDCODE_FAMILY_SHIFT; + if (tmp != maskid) { + ERROR("Incorrect IDCODE 0x%x, maskid 0x%x\n", id, maskid); + return "UNKN"; + } + VERBOSE("IDCODE 0x%x\n", id); + return zynqmp_get_silicon_idcode_name(); +} + +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; + } + + return SMC_ARCH_CALL_NOT_SUPPORTED; +} + +int32_t plat_get_soc_version(void) +{ + uint32_t chip_id = zynqmp_get_silicon_ver(); + uint32_t manfid = SOC_ID_SET_JEP_106(JEDEC_XILINX_BKID, JEDEC_XILINX_MFID); + + return (int32_t)(manfid | (chip_id & 0xFFFF)); +} + +int32_t plat_get_soc_revision(void) +{ + return mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET); +} + +static uint32_t zynqmp_get_ps_ver(void) +{ + uint32_t ver = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_VERSION_OFFSET); + + ver &= ZYNQMP_PS_VER_MASK; + ver >>= ZYNQMP_PS_VER_SHIFT; + + return ver + 1U; +} + +static void zynqmp_print_platform_name(void) +{ + uint32_t ver = zynqmp_get_silicon_ver(); + uint32_t rtl = zynqmp_get_rtl_ver(); + char *label = "Unknown"; + + switch (ver) { + case ZYNQMP_CSU_VERSION_QEMU: + label = "QEMU"; + break; + case ZYNQMP_CSU_VERSION_SILICON: + label = "silicon"; + break; + default: + /* Do nothing in default case */ + break; + } + + VERBOSE("TF-A running on %s/%s at 0x%x\n", + zynqmp_print_silicon_idcode(), label, BL31_BASE); + VERBOSE("TF-A running on v%d/RTL%d.%d\n", + zynqmp_get_ps_ver(), (rtl & 0xf0) >> 4, rtl & 0xf); +} +#else +static inline void zynqmp_print_platform_name(void) { } +#endif + +uint32_t zynqmp_get_bootmode(void) +{ + uint32_t r; + unsigned int ret; + + ret = pm_mmio_read(CRL_APB_BOOT_MODE_USER, &r); + + if (ret != PM_RET_SUCCESS) { + r = mmio_read_32(CRL_APB_BOOT_MODE_USER); + } + + return r & CRL_APB_BOOT_MODE_MASK; +} + +void zynqmp_config_setup(void) +{ + uint64_t counter_freq; + + /* Configure IPI data for ZynqMP */ + zynqmp_ipi_config_table_init(); + + zynqmp_print_platform_name(); + + /* Configure counter frequency */ + counter_freq = read_cntfrq_el0(); + if (counter_freq == ZYNQMP_DEFAULT_COUNTER_FREQ) { + write_cntfrq_el0(plat_get_syscnt_freq2()); + } + + generic_delay_timer_init(); +} + +uint32_t plat_get_syscnt_freq2(void) +{ + uint32_t ver = zynqmp_get_silicon_ver(); + + if (ver == ZYNQMP_CSU_VERSION_QEMU) { + return 65000000; + } else { + return mmio_read_32(IOU_SCNTRS_BASEFREQ); + } +} diff --git a/plat/xilinx/zynqmp/aarch64/zynqmp_helpers.S b/plat/xilinx/zynqmp/aarch64/zynqmp_helpers.S new file mode 100644 index 0000000..65aab52 --- /dev/null +++ b/plat/xilinx/zynqmp/aarch64/zynqmp_helpers.S @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <asm_macros.S> +#include <drivers/arm/gicv2.h> +#include <platform_def.h> + + .globl plat_secondary_cold_boot_setup + .globl plat_is_my_cpu_primary + .globl zynqmp_calc_core_pos + .globl plat_my_core_pos + .globl platform_mem_init + + /* ----------------------------------------------------- + * void plat_secondary_cold_boot_setup (void); + * + * This function performs any platform specific actions + * needed for a secondary cpu after a cold reset e.g + * mark the cpu's presence, mechanism to place it in a + * holding pen etc. + * TODO: Should we read the PSYS register to make sure + * that the request has gone through. + * ----------------------------------------------------- + */ +func plat_secondary_cold_boot_setup + mrs x0, mpidr_el1 + + /* Deactivate the gic cpu interface */ + ldr x1, =BASE_GICC_BASE + mov w0, #(IRQ_BYP_DIS_GRP1 | FIQ_BYP_DIS_GRP1) + orr w0, w0, #(IRQ_BYP_DIS_GRP0 | FIQ_BYP_DIS_GRP0) + str w0, [x1, #GICC_CTLR] + + /* + * There is no sane reason to come out of this wfi. This + * cpu will be powered on and reset by the cpu_on pm api + */ + dsb sy +1: + no_ret plat_panic_handler +endfunc plat_secondary_cold_boot_setup + +func plat_is_my_cpu_primary + mov x9, x30 + bl plat_my_core_pos + cmp x0, #ZYNQMP_PRIMARY_CPU + cset x0, eq + ret x9 +endfunc plat_is_my_cpu_primary + + /* ----------------------------------------------------- + * unsigned int plat_my_core_pos(void) + * This function uses the zynqmp_calc_core_pos() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_my_core_pos + mrs x0, mpidr_el1 + b zynqmp_calc_core_pos +endfunc plat_my_core_pos + + /* ----------------------------------------------------- + * unsigned int zynqmp_calc_core_pos(u_register_t mpidr) + * Helper function to calculate the core position. + * With this function: CorePos = (ClusterId * 4) + + * CoreId + * ----------------------------------------------------- + */ +func zynqmp_calc_core_pos + and x1, x0, #MPIDR_CPU_MASK + and x0, x0, #MPIDR_CLUSTER_MASK + add x0, x1, x0, LSR #6 + ret +endfunc zynqmp_calc_core_pos + + /* --------------------------------------------------------------------- + * We don't need to carry out any memory initialization on ARM + * platforms. The Secure RAM is accessible straight away. + * --------------------------------------------------------------------- + */ +func platform_mem_init + ret +endfunc platform_mem_init diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c new file mode 100644 index 0000000..baf6717 --- /dev/null +++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2013-2021, 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 <errno.h> + +#include <bl31/bl31.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <common/fdt_fixup.h> +#include <common/fdt_wrappers.h> +#include <lib/mmio.h> +#include <libfdt.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> +#include <plat_console.h> + +#include <custom_svc.h> +#include <plat_fdt.h> +#include <plat_private.h> +#include <plat_startup.h> +#include <zynqmp_def.h> + + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +/* + * Return a pointer to the 'entry_point_info' structure of the next image for + * the security state specified. BL33 corresponds to the non-secure image type + * while BL32 corresponds to the secure image type. A NULL pointer is returned + * if the image does not exist. + */ +struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type) +{ + entry_point_info_t *next_image_info; + + assert(sec_state_is_valid(type)); + if (type == NON_SECURE) { + next_image_info = &bl33_image_ep_info; + } else { + next_image_info = &bl32_image_ep_info; + } + + return next_image_info; +} + +/* + * Set the build time defaults. We want to do this when doing a JTAG boot + * or if we can't find any other config data. + */ +static inline void bl31_set_default_config(void) +{ + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = arm_get_spsr_for_bl32_entry(); + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); +} + +/* + * Perform any BL31 specific platform actions. Here is an opportunity to copy + * parameters passed by the calling EL (S-EL1 in BL2 & EL3 in BL1) before they + * are lost (potentially). This needs to be done before the MMU is initialized + * so that the memory layout can be used while creating page tables. + */ +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + uint64_t tfa_handoff_addr; + + setup_console(); + + /* Initialize the platform config for future decision making */ + zynqmp_config_setup(); + + /* + * Do initial security configuration to allow DRAM/device access. On + * Base ZYNQMP only DRAM security is programmable (via TrustZone), but + * other platforms might have more programmable security devices + * present. + */ + + /* Populate common information for BL32 and BL33 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + + tfa_handoff_addr = mmio_read_32(PMU_GLOBAL_GEN_STORAGE6); + + if (zynqmp_get_bootmode() == ZYNQMP_BOOTMODE_JTAG) { + bl31_set_default_config(); + } else { + /* use parameters from XBL */ + enum xbl_handoff ret = xbl_handover(&bl32_image_ep_info, + &bl33_image_ep_info, + tfa_handoff_addr); + if (ret != XBL_HANDOFF_SUCCESS) { + panic(); + } + } + if (bl32_image_ep_info.pc != 0) { + NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc); + } + if (bl33_image_ep_info.pc != 0) { + NOTICE("BL31: Non secure code at 0x%lx\n", bl33_image_ep_info.pc); + } + + custom_early_setup(); + +} + +#if ZYNQMP_WDT_RESTART +static zynmp_intr_info_type_el3_t type_el3_interrupt_table[MAX_INTR_EL3]; + +int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) +{ + static uint32_t index; + uint32_t i; + + /* Validate 'handler' and 'id' parameters */ + if (!handler || index >= MAX_INTR_EL3) { + return -EINVAL; + } + + /* Check if a handler has already been registered */ + for (i = 0; i < index; i++) { + if (id == type_el3_interrupt_table[i].id) { + return -EALREADY; + } + } + + type_el3_interrupt_table[index].id = id; + type_el3_interrupt_table[index].handler = handler; + + index++; + + return 0; +} + +static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + uint32_t intr_id; + uint32_t i; + interrupt_type_handler_t handler = NULL; + + intr_id = plat_ic_get_pending_interrupt_id(); + + for (i = 0; i < MAX_INTR_EL3; i++) { + if (intr_id == type_el3_interrupt_table[i].id) { + handler = type_el3_interrupt_table[i].handler; + } + } + + if (handler != NULL) { + return handler(intr_id, flags, handle, cookie); + } + + return 0; +} +#endif + +void bl31_platform_setup(void) +{ + prepare_dtb(); + + /* Initialize the gic cpu and distributor interfaces */ + plat_arm_gic_driver_init(); + plat_arm_gic_init(); +} + +void bl31_plat_runtime_setup(void) +{ +#if ZYNQMP_WDT_RESTART + uint64_t flags = 0; + uint64_t rc; + + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_EL3, + rdo_el3_interrupt_handler, flags); + if (rc) { + panic(); + } +#endif + + custom_runtime_setup(); + + console_switch_state(CONSOLE_FLAG_RUNTIME); +} + +/* + * Perform the very early platform specific architectural setup here. + */ +void bl31_plat_arch_setup(void) +{ + plat_arm_interconnect_init(); + plat_arm_interconnect_enter_coherency(); + + const mmap_region_t bl_regions[] = { +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) + MAP_REGION_FLAT(XILINX_OF_BOARD_DTB_ADDR, XILINX_OF_BOARD_DTB_MAX_SIZE, + MT_MEMORY | MT_RW | MT_NS), +#endif + MAP_REGION_FLAT(BL31_BASE, BL31_END - BL31_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), + MAP_REGION_FLAT(BL_COHERENT_RAM_BASE, + BL_COHERENT_RAM_END - BL_COHERENT_RAM_BASE, + MT_DEVICE | MT_RW | MT_SECURE), + {0} + }; + + custom_mmap_add(); + + setup_page_tables(bl_regions, plat_get_mmap()); + enable_mmu_el3(0); +} diff --git a/plat/xilinx/zynqmp/custom_sip_svc.c b/plat/xilinx/zynqmp/custom_sip_svc.c new file mode 100644 index 0000000..b9664af --- /dev/null +++ b/plat/xilinx/zynqmp/custom_sip_svc.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/bl_common.h> +#include <common/debug.h> +#include <smccc_helpers.h> + +uint64_t custom_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, + uint64_t x3, uint64_t x4, void *cookie, + void *handle, uint64_t flags) +{ + WARN("Unimplemented SiP Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); +} + +void custom_early_setup(void) +{ +} + +void custom_mmap_add(void) +{ +} + +void custom_runtime_setup(void) +{ +} diff --git a/plat/xilinx/zynqmp/include/custom_svc.h b/plat/xilinx/zynqmp/include/custom_svc.h new file mode 100644 index 0000000..242f3eb --- /dev/null +++ b/plat/xilinx/zynqmp/include/custom_svc.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef CUSTOM_SVC_H +#define CUSTOM_SVC_H + +#define ZYNQMP_SIP_SVC_CUSTOM U(0x82002000) +#define ZYNQMP_SIP_SVC64_CUSTOM U(0xC2002000) + +uint64_t custom_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, + uint64_t x3, uint64_t x4, void *cookie, + void *handle, uint64_t flags); + +void custom_early_setup(void); +void custom_mmap_add(void); +void custom_runtime_setup(void); + +#endif /* CUSTOM_SVC_H */ diff --git a/plat/xilinx/zynqmp/include/plat_ipi.h b/plat/xilinx/zynqmp/include/plat_ipi.h new file mode 100644 index 0000000..4007b91 --- /dev/null +++ b/plat/xilinx/zynqmp/include/plat_ipi.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-2018, 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 + */ + +/* ZynqMP IPI management enums and defines */ + +#ifndef PLAT_IPI_H +#define PLAT_IPI_H + +#include <stdint.h> +#include <ipi.h> + +/********************************************************************* + * IPI agent IDs macros + ********************************************************************/ +#define IPI_ID_APU 0U +#define IPI_ID_RPU0 1U +#define IPI_ID_RPU1 2U +#define IPI_ID_PMU0 3U +#define IPI_ID_PMU1 4U +#define IPI_ID_PMU2 5U +#define IPI_ID_PMU3 6U +#define IPI_ID_PL0 7U +#define IPI_ID_PL1 8U +#define IPI_ID_PL2 9U +#define IPI_ID_PL3 10U + +/********************************************************************* + * IPI message buffers + ********************************************************************/ +#define IPI_BUFFER_BASEADDR 0xFF990000U + +#define IPI_LOCAL_ID IPI_ID_APU +#define IPI_REMOTE_ID IPI_ID_PMU0 + +#define IPI_BUFFER_LOCAL_BASE (IPI_BUFFER_BASEADDR + 0x400U) +#define IPI_BUFFER_REMOTE_BASE (IPI_BUFFER_BASEADDR + 0xE00U) + +#define IPI_BUFFER_TARGET_LOCAL_OFFSET 0x80U +#define IPI_BUFFER_TARGET_REMOTE_OFFSET 0x1C0U + +#define IPI_BUFFER_MAX_WORDS 8U + +#define IPI_BUFFER_REQ_OFFSET 0x0U +#define IPI_BUFFER_RESP_OFFSET 0x20U + +/********************************************************************* + * Platform specific IPI API declarations + ********************************************************************/ + +/* Configure IPI table for zynqmp */ +void zynqmp_ipi_config_table_init(void); + +#endif /* PLAT_IPI_H */ diff --git a/plat/xilinx/zynqmp/include/plat_macros.S b/plat/xilinx/zynqmp/include/plat_macros.S new file mode 100644 index 0000000..c4ab619 --- /dev/null +++ b/plat/xilinx/zynqmp/include/plat_macros.S @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014-2018, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef PLAT_MACROS_S +#define PLAT_MACROS_S + +#include <arm_macros.S> +#include <cci_macros.S> +#include "zynqmp_def.h" + + /* --------------------------------------------- + * The below required platform porting macro + * prints out relevant GIC and CCI registers + * whenever an unhandled exception is taken in + * BL31. + * Clobbers: x0 - x10, x16, x17, sp + * --------------------------------------------- + */ + .macro plat_crash_print_regs + mov_imm x17, BASE_GICC_BASE + mov_imm x16, BASE_GICD_BASE + arm_print_gic_regs + print_cci_regs + .endm + +#endif /* PLAT_MACROS_S */ diff --git a/plat/xilinx/zynqmp/include/plat_pm_common.h b/plat/xilinx/zynqmp/include/plat_pm_common.h new file mode 100644 index 0000000..8731a20 --- /dev/null +++ b/plat/xilinx/zynqmp/include/plat_pm_common.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013-2020, 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 platform specific definitions of commonly used macros data types + * for PU Power Management. This file should be common for all PU's. + */ + +#ifndef PLAT_PM_COMMON_H +#define PLAT_PM_COMMON_H + +#include <stdint.h> +#include <common/debug.h> +#include "zynqmp_pm_defs.h" + + +#define ZYNQMP_TZ_VERSION_MAJOR 1 +#define ZYNQMP_TZ_VERSION_MINOR 0 +#define ZYNQMP_TZ_VERSION ((ZYNQMP_TZ_VERSION_MAJOR << 16) | \ + ZYNQMP_TZ_VERSION_MINOR) +#endif /* _PLAT_PM_COMMON_H_ */ diff --git a/plat/xilinx/zynqmp/include/plat_private.h b/plat/xilinx/zynqmp/include/plat_private.h new file mode 100644 index 0000000..afa102d --- /dev/null +++ b/plat/xilinx/zynqmp/include/plat_private.h @@ -0,0 +1,41 @@ +/* + * 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 + */ + +#ifndef PLAT_PRIVATE_H +#define PLAT_PRIVATE_H + +#include <stdint.h> + +#include <bl31/interrupt_mgmt.h> +#include <common/bl_common.h> +#include <drivers/cadence/cdns_uart.h> +#include <lib/xlat_tables/xlat_tables.h> + +void zynqmp_config_setup(void); + +const mmap_region_t *plat_get_mmap(void); + +uint32_t zynqmp_calc_core_pos(u_register_t mpidr); + +/* ZynqMP specific functions */ +uint32_t get_uart_clk(void); +uint32_t zynqmp_get_bootmode(void); + +#if ZYNQMP_WDT_RESTART +typedef struct zynqmp_intr_info_type_el3 { + uint32_t id; + interrupt_type_handler_t handler; +} zynmp_intr_info_type_el3_t; + +/* + * Register handler to specific GIC entrance + * for INTR_TYPE_EL3 type of interrupt + */ +int request_intr_type_el3(uint32_t, interrupt_type_handler_t); +#endif + +#endif /* PLAT_PRIVATE_H */ diff --git a/plat/xilinx/zynqmp/include/platform_def.h b/plat/xilinx/zynqmp/include/platform_def.h new file mode 100644 index 0000000..0c83a56 --- /dev/null +++ b/plat/xilinx/zynqmp/include/platform_def.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLATFORM_DEF_H +#define PLATFORM_DEF_H + +#include <arch.h> +#include <common/interrupt_props.h> +#include <drivers/arm/gic_common.h> +#include <lib/utils_def.h> + +#include "zynqmp_def.h" + +/******************************************************************************* + * Generic platform constants + ******************************************************************************/ + +/* Size of cacheable stacks */ +#ifndef PLATFORM_STACK_SIZE +#define PLATFORM_STACK_SIZE 0x440 +#endif + +#define PLATFORM_CORE_COUNT U(4) +#define PLAT_MAX_PWR_LVL U(1) +#define PLAT_MAX_RET_STATE U(1) +#define PLAT_MAX_OFF_STATE U(2) + +/******************************************************************************* + * BL31 specific defines. + ******************************************************************************/ +/* + * Put BL31 at the top of the Trusted SRAM (just below the shared memory, if + * present). BL31_BASE is calculated using the current BL31 debug size plus a + * little space for growth. + */ +#ifndef ZYNQMP_ATF_MEM_BASE +#if !DEBUG && defined(SPD_none) && !SDEI_SUPPORT +# define BL31_BASE U(0xfffea000) +# define BL31_LIMIT U(0x100000000) +#else +# define BL31_BASE U(0x1000) +# define BL31_LIMIT U(0x80000) +#endif +#else +# define BL31_BASE U(ZYNQMP_ATF_MEM_BASE) +# define BL31_LIMIT (UL(ZYNQMP_ATF_MEM_BASE) + U(ZYNQMP_ATF_MEM_SIZE)) +# ifdef ZYNQMP_ATF_MEM_PROGBITS_SIZE +# define BL31_PROGBITS_LIMIT (UL(ZYNQMP_ATF_MEM_BASE) + U(ZYNQMP_ATF_MEM_PROGBITS_SIZE)) +# endif +#endif + +/******************************************************************************* + * BL32 specific defines. + ******************************************************************************/ +#ifndef ZYNQMP_BL32_MEM_BASE +# define BL32_BASE U(0x60000000) +# define BL32_LIMIT U(0x80000000) +#else +# define BL32_BASE U(ZYNQMP_BL32_MEM_BASE) +# define BL32_LIMIT (UL(ZYNQMP_BL32_MEM_BASE) + U(ZYNQMP_BL32_MEM_SIZE)) +#endif + +/******************************************************************************* + * BL33 specific defines. + ******************************************************************************/ +#ifndef PRELOADED_BL33_BASE +# define PLAT_ARM_NS_IMAGE_BASE U(0x8000000) +#else +# define PLAT_ARM_NS_IMAGE_BASE U(PRELOADED_BL33_BASE) +#endif + +/******************************************************************************* + * TSP specific defines. + ******************************************************************************/ +#define TSP_SEC_MEM_BASE BL32_BASE +#define TSP_SEC_MEM_SIZE (BL32_LIMIT - BL32_BASE) + +/* ID of the secure physical generic timer interrupt used by the TSP */ +#define TSP_IRQ_SEC_PHY_TIMER ARM_IRQ_SEC_PHY_TIMER + +/******************************************************************************* + * Platform specific page table and MMU setup constants + ******************************************************************************/ +#define XILINX_OF_BOARD_DTB_MAX_SIZE U(0x200000) +#define PLAT_DDR_LOWMEM_MAX U(0x80000000) +#define PLAT_OCM_BASE U(0xFFFC0000) +#define PLAT_OCM_LIMIT U(0xFFFFFFFF) + +#define IS_TFA_IN_OCM(x) ((x >= PLAT_OCM_BASE) && (x < PLAT_OCM_LIMIT)) + +#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32) +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 32) + +#ifndef MAX_MMAP_REGIONS +#if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) +#define MAX_MMAP_REGIONS 8 +#else +#define MAX_MMAP_REGIONS 7 +#endif +#endif + +#ifndef MAX_XLAT_TABLES +#if !IS_TFA_IN_OCM(BL31_BASE) +#define MAX_XLAT_TABLES 8 +#else +#define MAX_XLAT_TABLES 5 +#endif +#endif + +#define CACHE_WRITEBACK_SHIFT 6 +#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) + +#define ZYNQMP_SDEI_SGI_PRIVATE U(8) + +/* Platform macros to support exception handling framework */ +#define PLAT_PRI_BITS U(3) +#define PLAT_SDEI_CRITICAL_PRI 0x10 +#define PLAT_SDEI_NORMAL_PRI 0x20 + +#define PLAT_ARM_GICD_BASE BASE_GICD_BASE +#define PLAT_ARM_GICC_BASE BASE_GICC_BASE +/* + * Define properties of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#if !ZYNQMP_WDT_RESTART +#define PLAT_ARM_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE) +#else +#define PLAT_ARM_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(IRQ_TTC3_1, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE) +#endif + +#define PLAT_ARM_G0_IRQ_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, PLAT_SDEI_NORMAL_PRI, grp, \ + GIC_INTR_CFG_EDGE) + +#endif /* PLATFORM_DEF_H */ diff --git a/plat/xilinx/zynqmp/include/zynqmp_def.h b/plat/xilinx/zynqmp/include/zynqmp_def.h new file mode 100644 index 0000000..d715ce2 --- /dev/null +++ b/plat/xilinx/zynqmp/include/zynqmp_def.h @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2014-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ZYNQMP_DEF_H +#define ZYNQMP_DEF_H + +#include <plat/arm/common/smccc_def.h> +#include <plat/common/common_def.h> + +#define ZYNQMP_CONSOLE_ID_cadence 1 +#define ZYNQMP_CONSOLE_ID_cadence0 1 +#define ZYNQMP_CONSOLE_ID_cadence1 2 +#define ZYNQMP_CONSOLE_ID_dcc 3 + +#define CONSOLE_IS(con) (ZYNQMP_CONSOLE_ID_ ## con == ZYNQMP_CONSOLE) + +/* Default counter frequency */ +#define ZYNQMP_DEFAULT_COUNTER_FREQ 0U + +/* Firmware Image Package */ +#define ZYNQMP_PRIMARY_CPU 0 + +/* Memory location options for Shared data and TSP in ZYNQMP */ +#define ZYNQMP_IN_TRUSTED_SRAM 0 +#define ZYNQMP_IN_TRUSTED_DRAM 1 + +/******************************************************************************* + * ZYNQMP memory map related constants + ******************************************************************************/ +/* Aggregate of all devices in the first GB */ +#define DEVICE0_BASE U(0xFF000000) +#define DEVICE0_SIZE U(0x00E00000) +#define DEVICE1_BASE U(0xF9000000) +#define DEVICE1_SIZE U(0x00800000) + +/* For cpu reset APU space here too 0xFE5F1000 CRF_APB*/ +#define CRF_APB_BASE U(0xFD1A0000) +#define CRF_APB_SIZE U(0x00600000) +#define CRF_APB_CLK_BASE U(0xFD1A0020) + +/* CRF registers and bitfields */ +#define CRF_APB_RST_FPD_APU (CRF_APB_BASE + 0X00000104) + +#define CRF_APB_RST_FPD_APU_ACPU_RESET (U(1) << 0) +#define CRF_APB_RST_FPD_APU_ACPU_PWRON_RESET (U(1) << 10) + +/* CRL registers and bitfields */ +#define CRL_APB_BASE U(0xFF5E0000) +#define CRL_APB_BOOT_MODE_USER (CRL_APB_BASE + 0x200) +#define CRL_APB_RESET_CTRL (CRL_APB_BASE + 0x218) +#define CRL_APB_RST_LPD_TOP (CRL_APB_BASE + 0x23C) +#define CRL_APB_BOOT_PIN_CTRL (CRL_APB_BASE + U(0x250)) +#define CRL_APB_CLK_BASE U(0xFF5E0020) + +#define CRL_APB_RPU_AMBA_RESET (U(1) << 2) +#define CRL_APB_RPLL_CTRL_BYPASS (U(1) << 3) + +#define CRL_APB_RESET_CTRL_SOFT_RESET (U(1) << 4) + +#define CRL_APB_BOOT_MODE_MASK (U(0xf) << 0) +#define CRL_APB_BOOT_PIN_MASK (U(0xf0f) << 0) +#define CRL_APB_BOOT_DRIVE_PIN_1_SHIFT U(9) +#define CRL_APB_BOOT_ENABLE_PIN_1_SHIFT U(1) +#define CRL_APB_BOOT_ENABLE_PIN_1 (U(0x1) << \ + CRL_APB_BOOT_ENABLE_PIN_1_SHIFT) +#define CRL_APB_BOOT_DRIVE_PIN_1 (U(0x1) << \ + CRL_APB_BOOT_DRIVE_PIN_1_SHIFT) +#define ZYNQMP_BOOTMODE_JTAG U(0) +#define ZYNQMP_ULPI_RESET_VAL_HIGH (CRL_APB_BOOT_ENABLE_PIN_1 | \ + CRL_APB_BOOT_DRIVE_PIN_1) +#define ZYNQMP_ULPI_RESET_VAL_LOW CRL_APB_BOOT_ENABLE_PIN_1 + +/* system counter registers and bitfields */ +#define IOU_SCNTRS_BASE U(0xFF260000) +#define IOU_SCNTRS_BASEFREQ (IOU_SCNTRS_BASE + 0x20) + +/* APU registers and bitfields */ +#define APU_BASE U(0xFD5C0000) +#define APU_CONFIG_0 (APU_BASE + 0x20) +#define APU_RVBAR_L_0 (APU_BASE + 0x40) +#define APU_RVBAR_H_0 (APU_BASE + 0x44) +#define APU_PWRCTL (APU_BASE + 0x90) + +#define APU_CONFIG_0_VINITHI_SHIFT 8 +#define APU_0_PWRCTL_CPUPWRDWNREQ_MASK 1 +#define APU_1_PWRCTL_CPUPWRDWNREQ_MASK 2 +#define APU_2_PWRCTL_CPUPWRDWNREQ_MASK 4 +#define APU_3_PWRCTL_CPUPWRDWNREQ_MASK 8 + +/* PMU registers and bitfields */ +#define PMU_GLOBAL_BASE U(0xFFD80000) +#define PMU_GLOBAL_CNTRL (PMU_GLOBAL_BASE + 0) +#define PMU_GLOBAL_GEN_STORAGE6 (PMU_GLOBAL_BASE + 0x48) +#define PMU_GLOBAL_REQ_PWRUP_STATUS (PMU_GLOBAL_BASE + 0x110) +#define PMU_GLOBAL_REQ_PWRUP_EN (PMU_GLOBAL_BASE + 0x118) +#define PMU_GLOBAL_REQ_PWRUP_DIS (PMU_GLOBAL_BASE + 0x11c) +#define PMU_GLOBAL_REQ_PWRUP_TRIG (PMU_GLOBAL_BASE + 0x120) + +#define PMU_GLOBAL_CNTRL_FW_IS_PRESENT (1 << 4) + +/******************************************************************************* + * CCI-400 related constants + ******************************************************************************/ +#define PLAT_ARM_CCI_BASE U(0xFD6E0000) +#define PLAT_ARM_CCI_CLUSTER0_SL_IFACE_IX 3 +#define PLAT_ARM_CCI_CLUSTER1_SL_IFACE_IX 4 + +/******************************************************************************* + * GIC-400 & interrupt handling related constants + ******************************************************************************/ +#define BASE_GICD_BASE U(0xF9010000) +#define BASE_GICC_BASE U(0xF9020000) +#define BASE_GICH_BASE U(0xF9040000) +#define BASE_GICV_BASE U(0xF9060000) + +#if ZYNQMP_WDT_RESTART +#define IRQ_SEC_IPI_APU 67 +#define IRQ_TTC3_1 77 +#define TTC3_BASE_ADDR U(0xFF140000) +#define TTC3_INTR_REGISTER_1 (TTC3_BASE_ADDR + 0x54) +#define TTC3_INTR_ENABLE_1 (TTC3_BASE_ADDR + 0x60) +#endif + +#define ARM_IRQ_SEC_PHY_TIMER 29 + +#define ARM_IRQ_SEC_SGI_0 8 +#define ARM_IRQ_SEC_SGI_1 9 +#define ARM_IRQ_SEC_SGI_2 10 +#define ARM_IRQ_SEC_SGI_3 11 +#define ARM_IRQ_SEC_SGI_4 12 +#define ARM_IRQ_SEC_SGI_5 13 +#define ARM_IRQ_SEC_SGI_6 14 +#define ARM_IRQ_SEC_SGI_7 15 + +/* number of interrupt handlers. increase as required */ +#define MAX_INTR_EL3 2 + +/******************************************************************************* + * UART related constants + ******************************************************************************/ +#define ZYNQMP_UART0_BASE U(0xFF000000) +#define ZYNQMP_UART1_BASE U(0xFF010000) + +#if CONSOLE_IS(cadence) || CONSOLE_IS(dcc) +# define UART_BASE ZYNQMP_UART0_BASE +#elif CONSOLE_IS(cadence1) +# define UART_BASE ZYNQMP_UART1_BASE +#else +# error "invalid ZYNQMP_CONSOLE" +#endif + +/* Must be non zero */ +#define UART_BAUDRATE 115200 + +/* Silicon version detection */ +#define ZYNQMP_SILICON_VER_MASK 0xF000 +#define ZYNQMP_SILICON_VER_SHIFT 12 +#define ZYNQMP_CSU_VERSION_SILICON 0 +#define ZYNQMP_CSU_VERSION_QEMU 3 + +#define ZYNQMP_RTL_VER_MASK 0xFF0U +#define ZYNQMP_RTL_VER_SHIFT 4 + +#define ZYNQMP_PS_VER_MASK 0xFU +#define ZYNQMP_PS_VER_SHIFT 0 + +#define ZYNQMP_CSU_BASEADDR U(0xFFCA0000) +#define ZYNQMP_CSU_IDCODE_OFFSET 0x40U + +#define ZYNQMP_CSU_IDCODE_XILINX_ID_SHIFT 0U +#define ZYNQMP_CSU_IDCODE_XILINX_ID_MASK (0xFFFU << \ + ZYNQMP_CSU_IDCODE_XILINX_ID_SHIFT) +#define ZYNQMP_CSU_IDCODE_XILINX_ID 0x093 + +#define ZYNQMP_CSU_IDCODE_SVD_SHIFT 12U +#define ZYNQMP_CSU_IDCODE_SVD_MASK (0x7U << \ + ZYNQMP_CSU_IDCODE_SVD_SHIFT) +#define ZYNQMP_CSU_IDCODE_DEVICE_CODE_SHIFT 15U +#define ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK (0xFU << \ + ZYNQMP_CSU_IDCODE_DEVICE_CODE_SHIFT) +#define ZYNQMP_CSU_IDCODE_SUB_FAMILY_SHIFT 19U +#define ZYNQMP_CSU_IDCODE_SUB_FAMILY_MASK (0x3U << \ + ZYNQMP_CSU_IDCODE_SUB_FAMILY_SHIFT) +#define ZYNQMP_CSU_IDCODE_FAMILY_SHIFT 21U +#define ZYNQMP_CSU_IDCODE_FAMILY_MASK (0x7FU << \ + ZYNQMP_CSU_IDCODE_FAMILY_SHIFT) +#define ZYNQMP_CSU_IDCODE_FAMILY 0x23 + +#define ZYNQMP_CSU_IDCODE_REVISION_SHIFT 28U +#define ZYNQMP_CSU_IDCODE_REVISION_MASK (0xFU << \ + ZYNQMP_CSU_IDCODE_REVISION_SHIFT) +#define ZYNQMP_CSU_IDCODE_REVISION 0U + +#define ZYNQMP_CSU_VERSION_OFFSET 0x44U + +/* Efuse */ +#define EFUSE_BASEADDR U(0xFFCC0000) +#define EFUSE_IPDISABLE_OFFSET 0x1018 +#define EFUSE_IPDISABLE_VERSION 0x1FFU +#define ZYNQMP_EFUSE_IPDISABLE_SHIFT 20 + +/* Access control register defines */ +#define ACTLR_EL3_L2ACTLR_BIT (1 << 6) +#define ACTLR_EL3_CPUACTLR_BIT (1 << 0) + +#define FPD_SLCR_BASEADDR U(0xFD610000) +#define IOU_SLCR_BASEADDR U(0xFF180000) + +#define ZYNQMP_RPU_GLBL_CNTL U(0xFF9A0000) +#define ZYNQMP_RPU0_CFG U(0xFF9A0100) +#define ZYNQMP_RPU1_CFG U(0xFF9A0200) +#define ZYNQMP_SLSPLIT_MASK U(0x08) +#define ZYNQMP_TCM_COMB_MASK U(0x40) +#define ZYNQMP_SLCLAMP_MASK U(0x10) +#define ZYNQMP_VINITHI_MASK U(0x04) + +/* Tap delay bypass */ +#define IOU_TAPDLY_BYPASS U(0XFF180390) +#define TAP_DELAY_MASK U(0x7) + +/* SD DLL reset */ +#define ZYNQMP_SD_DLL_CTRL U(0xFF180358) +#define ZYNQMP_SD0_DLL_RST_MASK U(0x00000004) +#define ZYNQMP_SD0_DLL_RST U(0x00000004) +#define ZYNQMP_SD1_DLL_RST_MASK U(0x00040000) +#define ZYNQMP_SD1_DLL_RST U(0x00040000) + +/* SD tap delay */ +#define ZYNQMP_SD_DLL_CTRL U(0xFF180358) +#define ZYNQMP_SD_ITAP_DLY U(0xFF180314) +#define ZYNQMP_SD_OTAP_DLY U(0xFF180318) +#define ZYNQMP_SD_TAP_OFFSET U(16) +#define ZYNQMP_SD_ITAPCHGWIN_MASK U(0x200) +#define ZYNQMP_SD_ITAPCHGWIN U(0x200) +#define ZYNQMP_SD_ITAPDLYENA_MASK U(0x100) +#define ZYNQMP_SD_ITAPDLYENA U(0x100) +#define ZYNQMP_SD_ITAPDLYSEL_MASK U(0xFF) +#define ZYNQMP_SD_OTAPDLYSEL_MASK U(0x3F) +#define ZYNQMP_SD_OTAPDLYENA_MASK U(0x40) +#define ZYNQMP_SD_OTAPDLYENA U(0x40) + +/* Clock control registers */ +/* Full power domain clocks */ +#define CRF_APB_APLL_CTRL (CRF_APB_CLK_BASE + 0x00) +#define CRF_APB_DPLL_CTRL (CRF_APB_CLK_BASE + 0x0c) +#define CRF_APB_VPLL_CTRL (CRF_APB_CLK_BASE + 0x18) +#define CRF_APB_PLL_STATUS (CRF_APB_CLK_BASE + 0x24) +#define CRF_APB_APLL_TO_LPD_CTRL (CRF_APB_CLK_BASE + 0x28) +#define CRF_APB_DPLL_TO_LPD_CTRL (CRF_APB_CLK_BASE + 0x2c) +#define CRF_APB_VPLL_TO_LPD_CTRL (CRF_APB_CLK_BASE + 0x30) +/* Peripheral clocks */ +#define CRF_APB_ACPU_CTRL (CRF_APB_CLK_BASE + 0x40) +#define CRF_APB_DBG_TRACE_CTRL (CRF_APB_CLK_BASE + 0x44) +#define CRF_APB_DBG_FPD_CTRL (CRF_APB_CLK_BASE + 0x48) +#define CRF_APB_DP_VIDEO_REF_CTRL (CRF_APB_CLK_BASE + 0x50) +#define CRF_APB_DP_AUDIO_REF_CTRL (CRF_APB_CLK_BASE + 0x54) +#define CRF_APB_DP_STC_REF_CTRL (CRF_APB_CLK_BASE + 0x5c) +#define CRF_APB_DDR_CTRL (CRF_APB_CLK_BASE + 0x60) +#define CRF_APB_GPU_REF_CTRL (CRF_APB_CLK_BASE + 0x64) +#define CRF_APB_SATA_REF_CTRL (CRF_APB_CLK_BASE + 0x80) +#define CRF_APB_PCIE_REF_CTRL (CRF_APB_CLK_BASE + 0x94) +#define CRF_APB_GDMA_REF_CTRL (CRF_APB_CLK_BASE + 0x98) +#define CRF_APB_DPDMA_REF_CTRL (CRF_APB_CLK_BASE + 0x9c) +#define CRF_APB_TOPSW_MAIN_CTRL (CRF_APB_CLK_BASE + 0xa0) +#define CRF_APB_TOPSW_LSBUS_CTRL (CRF_APB_CLK_BASE + 0xa4) +#define CRF_APB_GTGREF0_REF_CTRL (CRF_APB_CLK_BASE + 0xa8) +#define CRF_APB_DBG_TSTMP_CTRL (CRF_APB_CLK_BASE + 0xd8) + +/* Low power domain clocks */ +#define CRL_APB_IOPLL_CTRL (CRL_APB_CLK_BASE + 0x00) +#define CRL_APB_RPLL_CTRL (CRL_APB_CLK_BASE + 0x10) +#define CRL_APB_PLL_STATUS (CRL_APB_CLK_BASE + 0x20) +#define CRL_APB_IOPLL_TO_FPD_CTRL (CRL_APB_CLK_BASE + 0x24) +#define CRL_APB_RPLL_TO_FPD_CTRL (CRL_APB_CLK_BASE + 0x28) +/* Peripheral clocks */ +#define CRL_APB_USB3_DUAL_REF_CTRL (CRL_APB_CLK_BASE + 0x2c) +#define CRL_APB_GEM0_REF_CTRL (CRL_APB_CLK_BASE + 0x30) +#define CRL_APB_GEM1_REF_CTRL (CRL_APB_CLK_BASE + 0x34) +#define CRL_APB_GEM2_REF_CTRL (CRL_APB_CLK_BASE + 0x38) +#define CRL_APB_GEM3_REF_CTRL (CRL_APB_CLK_BASE + 0x3c) +#define CRL_APB_USB0_BUS_REF_CTRL (CRL_APB_CLK_BASE + 0x40) +#define CRL_APB_USB1_BUS_REF_CTRL (CRL_APB_CLK_BASE + 0x44) +#define CRL_APB_QSPI_REF_CTRL (CRL_APB_CLK_BASE + 0x48) +#define CRL_APB_SDIO0_REF_CTRL (CRL_APB_CLK_BASE + 0x4c) +#define CRL_APB_SDIO1_REF_CTRL (CRL_APB_CLK_BASE + 0x50) +#define CRL_APB_UART0_REF_CTRL (CRL_APB_CLK_BASE + 0x54) +#define CRL_APB_UART1_REF_CTRL (CRL_APB_CLK_BASE + 0x58) +#define CRL_APB_SPI0_REF_CTRL (CRL_APB_CLK_BASE + 0x5c) +#define CRL_APB_SPI1_REF_CTRL (CRL_APB_CLK_BASE + 0x60) +#define CRL_APB_CAN0_REF_CTRL (CRL_APB_CLK_BASE + 0x64) +#define CRL_APB_CAN1_REF_CTRL (CRL_APB_CLK_BASE + 0x68) +#define CRL_APB_CPU_R5_CTRL (CRL_APB_CLK_BASE + 0x70) +#define CRL_APB_IOU_SWITCH_CTRL (CRL_APB_CLK_BASE + 0x7c) +#define CRL_APB_CSU_PLL_CTRL (CRL_APB_CLK_BASE + 0x80) +#define CRL_APB_PCAP_CTRL (CRL_APB_CLK_BASE + 0x84) +#define CRL_APB_LPD_SWITCH_CTRL (CRL_APB_CLK_BASE + 0x88) +#define CRL_APB_LPD_LSBUS_CTRL (CRL_APB_CLK_BASE + 0x8c) +#define CRL_APB_DBG_LPD_CTRL (CRL_APB_CLK_BASE + 0x90) +#define CRL_APB_NAND_REF_CTRL (CRL_APB_CLK_BASE + 0x94) +#define CRL_APB_ADMA_REF_CTRL (CRL_APB_CLK_BASE + 0x98) +#define CRL_APB_PL0_REF_CTRL (CRL_APB_CLK_BASE + 0xa0) +#define CRL_APB_PL1_REF_CTRL (CRL_APB_CLK_BASE + 0xa4) +#define CRL_APB_PL2_REF_CTRL (CRL_APB_CLK_BASE + 0xa8) +#define CRL_APB_PL3_REF_CTRL (CRL_APB_CLK_BASE + 0xac) +#define CRL_APB_PL0_THR_CNT (CRL_APB_CLK_BASE + 0xb4) +#define CRL_APB_PL1_THR_CNT (CRL_APB_CLK_BASE + 0xbc) +#define CRL_APB_PL2_THR_CNT (CRL_APB_CLK_BASE + 0xc4) +#define CRL_APB_PL3_THR_CNT (CRL_APB_CLK_BASE + 0xdc) +#define CRL_APB_GEM_TSU_REF_CTRL (CRL_APB_CLK_BASE + 0xe0) +#define CRL_APB_DLL_REF_CTRL (CRL_APB_CLK_BASE + 0xe4) +#define CRL_APB_AMS_REF_CTRL (CRL_APB_CLK_BASE + 0xe8) +#define CRL_APB_I2C0_REF_CTRL (CRL_APB_CLK_BASE + 0x100) +#define CRL_APB_I2C1_REF_CTRL (CRL_APB_CLK_BASE + 0x104) +#define CRL_APB_TIMESTAMP_REF_CTRL (CRL_APB_CLK_BASE + 0x108) +#define IOU_SLCR_GEM_CLK_CTRL (IOU_SLCR_BASEADDR + 0x308) +#define IOU_SLCR_CAN_MIO_CTRL (IOU_SLCR_BASEADDR + 0x304) +#define FPD_SLCR_WDT_CLK_SEL (FPD_SLCR_BASEADDR + 0x100) +#define IOU_SLCR_WDT_CLK_SEL (IOU_SLCR_BASEADDR + 0x300) + +/* Global general storage register base address */ +#define GGS_BASEADDR (0xFFD80030U) +#define GGS_NUM_REGS U(4) + +/* Persistent global general storage register base address */ +#define PGGS_BASEADDR (0xFFD80050U) +#define PGGS_NUM_REGS U(4) + +/* PMU GGS4 register 4 is used for warm restart boot health status */ +#define PMU_GLOBAL_GEN_STORAGE4 (GGS_BASEADDR + 0x10) +/* Warm restart boot health status mask */ +#define PM_BOOT_HEALTH_STATUS_MASK U(0x01) +/* WDT restart scope shift and mask */ +#define RESTART_SCOPE_SHIFT (3) +#define RESTART_SCOPE_MASK (0x3U << RESTART_SCOPE_SHIFT) + +/* AFI registers */ +#define AFIFM6_WRCTRL U(13) +#define FABRIC_WIDTH U(3) + +/* CSUDMA Module Base Address*/ +#define CSUDMA_BASE U(0xFFC80000) + +/* RSA-CORE Module Base Address*/ +#define RSA_CORE_BASE U(0xFFCE0000) + +#endif /* ZYNQMP_DEF_H */ diff --git a/plat/xilinx/zynqmp/libpm.mk b/plat/xilinx/zynqmp/libpm.mk new file mode 100644 index 0000000..db3c742 --- /dev/null +++ b/plat/xilinx/zynqmp/libpm.mk @@ -0,0 +1,18 @@ +# +# Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +LIBPM_SRCS := $(addprefix plat/xilinx/common/pm_service/, \ + pm_ipi.c) + +LIBPM_SRCS += $(addprefix plat/xilinx/zynqmp/pm_service/, \ + zynqmp_pm_svc_main.c \ + zynqmp_pm_api_sys.c \ + pm_api_pinctrl.c \ + pm_api_ioctl.c \ + pm_api_clock.c \ + pm_client.c) + +$(eval $(call MAKE_LIB,pm)) diff --git a/plat/xilinx/zynqmp/plat_psci.c b/plat/xilinx/zynqmp/plat_psci.c new file mode 100644 index 0000000..c6c6c4b --- /dev/null +++ b/plat/xilinx/zynqmp/plat_psci.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/arm/gicv2.h> +#include <lib/mmio.h> +#include <lib/psci/psci.h> +#include <plat/arm/common/plat_arm.h> +#include <plat/common/platform.h> + +#include <plat_private.h> +#include "pm_client.h" +#include "zynqmp_pm_api_sys.h" + +static uintptr_t zynqmp_sec_entry; + +static void zynqmp_cpu_standby(plat_local_state_t cpu_state) +{ + VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state); + + dsb(); + wfi(); +} + +static int32_t zynqmp_pwr_domain_on(u_register_t mpidr) +{ + uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr); + const struct pm_proc *proc; + uint32_t buff[3]; + enum pm_ret_status ret; + + VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); + + if (cpu_id == -1) { + return PSCI_E_INTERN_FAIL; + } + proc = pm_get_proc(cpu_id); + + /* Check the APU proc status before wakeup */ + ret = pm_get_node_status(proc->node_id, buff); + if ((ret != PM_RET_SUCCESS) || (buff[0] == PM_PROC_STATE_SUSPENDING)) { + return PSCI_E_INTERN_FAIL; + } + + /* Clear power down request */ + pm_client_wakeup(proc); + + /* Send request to PMU to wake up selected APU CPU core */ + pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING); + + return PSCI_E_SUCCESS; +} + +static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state) +{ + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + + /* Prevent interrupts from spuriously waking up this cpu */ + gicv2_cpuif_disable(); + + /* + * Send request to PMU to power down the appropriate APU CPU + * core. + * According to PSCI specification, CPU_off function does not + * have resume address and CPU core can only be woken up + * invoking CPU_on function, during which resume address will + * be set. + */ + pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0); +} + +static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + uint32_t state; + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + + state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? + PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; + + /* Send request to PMU to suspend this core */ + pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry); + + /* APU is to be turned off */ + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + /* disable coherency */ + plat_arm_interconnect_exit_coherency(); + } +} + +static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + plat_arm_gic_pcpu_init(); + gicv2_cpuif_enable(); +} + +static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) +{ + uint32_t cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + } + + /* Clear the APU power control register for this cpu */ + pm_client_wakeup(proc); + + /* enable coherency */ + plat_arm_interconnect_enter_coherency(); + /* APU was turned off */ + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + plat_arm_gic_init(); + } else { + gicv2_cpuif_enable(); + gicv2_pcpu_distif_init(); + } +} + +/******************************************************************************* + * ZynqMP handlers to shutdown/reboot the system + ******************************************************************************/ + +static void __dead2 zynqmp_system_off(void) +{ + /* disable coherency */ + plat_arm_interconnect_exit_coherency(); + + /* Send the power down request to the PMU */ + pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN, + pm_get_shutdown_scope()); + + while (1) { + wfi(); + } +} + +static void __dead2 zynqmp_system_reset(void) +{ + /* disable coherency */ + plat_arm_interconnect_exit_coherency(); + + /* Send the system reset request to the PMU */ + pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET, + pm_get_shutdown_scope()); + + while (1) { + wfi(); + } +} + +static int32_t zynqmp_validate_power_state(uint32_t power_state, + psci_power_state_t *req_state) +{ + VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); + + uint32_t pstate = psci_get_pstate_type(power_state); + + assert(req_state); + + /* Sanity check the requested state */ + if (pstate == PSTATE_TYPE_STANDBY) { + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; + } else { + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; + } + /* We expect the 'state id' to be zero */ + if (psci_get_pstate_id(power_state)) { + return PSCI_E_INVALID_PARAMS; + } + + return PSCI_E_SUCCESS; +} + +static void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; + req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; +} + +/******************************************************************************* + * Export the platform handlers to enable psci to invoke them + ******************************************************************************/ +static const struct plat_psci_ops zynqmp_psci_ops = { + .cpu_standby = zynqmp_cpu_standby, + .pwr_domain_on = zynqmp_pwr_domain_on, + .pwr_domain_off = zynqmp_pwr_domain_off, + .pwr_domain_suspend = zynqmp_pwr_domain_suspend, + .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish, + .pwr_domain_suspend_finish = zynqmp_pwr_domain_suspend_finish, + .system_off = zynqmp_system_off, + .system_reset = zynqmp_system_reset, + .validate_power_state = zynqmp_validate_power_state, + .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state, +}; + +/******************************************************************************* + * Export the platform specific power ops. + ******************************************************************************/ +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const struct plat_psci_ops **psci_ops) +{ + zynqmp_sec_entry = sec_entrypoint; + + *psci_ops = &zynqmp_psci_ops; + + return 0; +} diff --git a/plat/xilinx/zynqmp/plat_topology.c b/plat/xilinx/zynqmp/plat_topology.c new file mode 100644 index 0000000..2596650 --- /dev/null +++ b/plat/xilinx/zynqmp/plat_topology.c @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2013-2016, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <stdint.h> + +static const uint8_t plat_power_domain_tree_desc[] = {1, 4}; + +const uint8_t *plat_get_power_domain_tree_desc(void) +{ + return plat_power_domain_tree_desc; +} diff --git a/plat/xilinx/zynqmp/plat_zynqmp.c b/plat/xilinx/zynqmp/plat_zynqmp.c new file mode 100644 index 0000000..e3a979e --- /dev/null +++ b/plat/xilinx/zynqmp/plat_zynqmp.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <plat/common/platform.h> + +#include <plat_private.h> + +int32_t plat_core_pos_by_mpidr(u_register_t mpidr) +{ + if (mpidr & MPIDR_CLUSTER_MASK) { + return -1; + } + + if ((mpidr & MPIDR_CPU_MASK) >= PLATFORM_CORE_COUNT) { + return -1; + } + + return zynqmp_calc_core_pos(mpidr); +} diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk new file mode 100644 index 0000000..e266615 --- /dev/null +++ b/plat/xilinx/zynqmp/platform.mk @@ -0,0 +1,159 @@ +# +# Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved. +# Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved. +# Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved. +# Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +override ERRATA_A53_855873 := 1 +ERRATA_A53_1530924 := 1 +override PROGRAMMABLE_RESET_ADDRESS := 1 +PSCI_EXTENDED_STATE_ID := 1 +A53_DISABLE_NON_TEMPORAL_HINT := 0 +SEPARATE_CODE_AND_RODATA := 1 +ZYNQMP_WDT_RESTART := 0 +IPI_CRC_CHECK := 0 +override RESET_TO_BL31 := 1 +override WARMBOOT_ENABLE_DCACHE_EARLY := 1 + +EL3_EXCEPTION_HANDLING := $(SDEI_SUPPORT) + +# pncd SPD requires secure SGI to be handled at EL1 +ifeq (${SPD}, $(filter ${SPD},pncd tspd)) +ifeq (${ZYNQMP_WDT_RESTART},1) +$(error "Error: ZYNQMP_WDT_RESTART and SPD=pncd are incompatible") +endif +override GICV2_G0_FOR_EL3 := 0 +else +override GICV2_G0_FOR_EL3 := 1 +endif + +# Do not enable SVE +ENABLE_SVE_FOR_NS := 0 + +WORKAROUND_CVE_2017_5715 := 0 + +ARM_XLAT_TABLES_LIB_V1 := 1 +$(eval $(call assert_boolean,ARM_XLAT_TABLES_LIB_V1)) +$(eval $(call add_define,ARM_XLAT_TABLES_LIB_V1)) + +ifdef ZYNQMP_ATF_MEM_BASE + $(eval $(call add_define,ZYNQMP_ATF_MEM_BASE)) + + ifndef ZYNQMP_ATF_MEM_SIZE + $(error "ZYNQMP_ATF_BASE defined without ZYNQMP_ATF_SIZE") + endif + $(eval $(call add_define,ZYNQMP_ATF_MEM_SIZE)) + + ifdef ZYNQMP_ATF_MEM_PROGBITS_SIZE + $(eval $(call add_define,ZYNQMP_ATF_MEM_PROGBITS_SIZE)) + endif + + # enable assert() when TF-A runs from DDR memory. + ENABLE_ASSERTIONS := 1 + +endif + +ifdef ZYNQMP_BL32_MEM_BASE + $(eval $(call add_define,ZYNQMP_BL32_MEM_BASE)) + + ifndef ZYNQMP_BL32_MEM_SIZE + $(error "ZYNQMP_BL32_BASE defined without ZYNQMP_BL32_SIZE") + endif + $(eval $(call add_define,ZYNQMP_BL32_MEM_SIZE)) +endif + + +ifdef ZYNQMP_WDT_RESTART + $(eval $(call add_define,ZYNQMP_WDT_RESTART)) +endif + +ifdef ZYNQMP_IPI_CRC_CHECK + $(warning "ZYNQMP_IPI_CRC_CHECK macro is deprecated...instead please use IPI_CRC_CHECK.") +endif + +ifdef IPI_CRC_CHECK + $(eval $(call add_define,IPI_CRC_CHECK)) +endif + +ifdef ZYNQMP_SECURE_EFUSES + $(eval $(call add_define,ZYNQMP_SECURE_EFUSES)) +endif + +ifdef XILINX_OF_BOARD_DTB_ADDR +$(eval $(call add_define,XILINX_OF_BOARD_DTB_ADDR)) +endif + +PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ + -Iinclude/plat/arm/common/aarch64/ \ + -Iplat/xilinx/common/include/ \ + -Iplat/xilinx/common/ipi_mailbox_service/ \ + -Iplat/xilinx/zynqmp/include/ \ + -Iplat/xilinx/zynqmp/pm_service/ \ + +include lib/libfdt/libfdt.mk +# Include GICv2 driver files +include drivers/arm/gic/v2/gicv2.mk + +PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ + lib/xlat_tables/aarch64/xlat_tables.c \ + drivers/arm/dcc/dcc_console.c \ + drivers/delay_timer/delay_timer.c \ + drivers/delay_timer/generic_delay_timer.c \ + ${GICV2_SOURCES} \ + drivers/cadence/uart/aarch64/cdns_console.S \ + plat/arm/common/arm_cci.c \ + plat/arm/common/arm_common.c \ + plat/arm/common/arm_gicv2.c \ + plat/common/plat_gicv2.c \ + plat/xilinx/common/ipi.c \ + plat/xilinx/zynqmp/zynqmp_ipi.c \ + plat/common/aarch64/crash_console_helpers.S \ + plat/xilinx/zynqmp/aarch64/zynqmp_helpers.S \ + plat/xilinx/zynqmp/aarch64/zynqmp_common.c + +ZYNQMP_CONSOLE ?= cadence +ifeq (${ZYNQMP_CONSOLE}, $(filter ${ZYNQMP_CONSOLE},cadence cadence0 cadence1 dcc)) +else + $(error "Please define ZYNQMP_CONSOLE") +endif +$(eval $(call add_define_val,ZYNQMP_CONSOLE,ZYNQMP_CONSOLE_ID_${ZYNQMP_CONSOLE})) + +# Build PM code as a Library +include plat/xilinx/zynqmp/libpm.mk + +BL31_SOURCES += drivers/arm/cci/cci.c \ + lib/cpus/aarch64/aem_generic.S \ + lib/cpus/aarch64/cortex_a53.S \ + plat/common/plat_psci_common.c \ + common/fdt_fixup.c \ + common/fdt_wrappers.c \ + ${LIBFDT_SRCS} \ + plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ + plat/xilinx/common/plat_startup.c \ + plat/xilinx/common/plat_console.c \ + plat/xilinx/common/plat_fdt.c \ + plat/xilinx/zynqmp/bl31_zynqmp_setup.c \ + plat/xilinx/zynqmp/plat_psci.c \ + plat/xilinx/zynqmp/plat_zynqmp.c \ + plat/xilinx/zynqmp/plat_topology.c \ + plat/xilinx/zynqmp/sip_svc_setup.c + +ifeq (${SDEI_SUPPORT},1) +BL31_SOURCES += plat/xilinx/zynqmp/zynqmp_ehf.c \ + plat/xilinx/zynqmp/zynqmp_sdei.c +endif + +BL31_CPPFLAGS += -fno-jump-tables +TF_CFLAGS_aarch64 += -mbranch-protection=none + +ifdef CUSTOM_PKG_PATH +include $(CUSTOM_PKG_PATH)/custom_pkg.mk +else +BL31_SOURCES += plat/xilinx/zynqmp/custom_sip_svc.c +endif + +ifneq (${RESET_TO_BL31},1) + $(error "Using BL31 as the reset vector is only one option supported on ZynqMP. Please set RESET_TO_BL31 to 1.") +endif diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c new file mode 100644 index 0000000..9682e59 --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -0,0 +1,3075 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * ZynqMP system level PM-API functions for clock control. + */ + +#include <stdbool.h> +#include <string.h> + +#include <arch_helpers.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#include "pm_api_clock.h" +#include "pm_client.h" +#include "pm_common.h" +#include "pm_ipi.h" +#include "zynqmp_pm_api_sys.h" + +#define CLK_NODE_MAX (6U) + +#define CLK_PARENTS_ID_LEN (16U) +#define CLK_TOPOLOGY_NODE_OFFSET (16U) +#define CLK_TOPOLOGY_PAYLOAD_LEN (12U) +#define CLK_PARENTS_PAYLOAD_LEN (12U) +#define CLK_TYPE_SHIFT (2U) +#define CLK_CLKFLAGS_SHIFT (8U) +#define CLK_TYPEFLAGS_SHIFT (24U) +#define CLK_TYPEFLAGS2_SHIFT (4U) +#define CLK_TYPEFLAGS_BITS_MASK (0xFFU) +#define CLK_TYPEFLAGS2_BITS_MASK (0x0F00U) +#define CLK_TYPEFLAGS_BITS (8U) + +#define CLK_EXTERNAL_PARENT (PARENT_CLK_EXTERNAL << CLK_PARENTS_ID_LEN) + +#define NA_MULT (0U) +#define NA_DIV (0U) +#define NA_SHIFT (0U) +#define NA_WIDTH (0U) +#define NA_CLK_FLAGS (0U) +#define NA_TYPE_FLAGS (0U) + +/* PLL nodes related definitions */ +#define PLL_PRESRC_MUX_SHIFT (20U) +#define PLL_PRESRC_MUX_WIDTH (3U) +#define PLL_POSTSRC_MUX_SHIFT (24U) +#define PLL_POSTSRC_MUX_WIDTH (3U) +#define PLL_DIV2_MUX_SHIFT (16U) +#define PLL_DIV2_MUX_WIDTH (1U) +#define PLL_BYPASS_MUX_SHIFT (3U) +#define PLL_BYPASS_MUX_WIDTH (1U) + +/* Peripheral nodes related definitions */ +/* Peripheral Clocks */ +#define PERIPH_MUX_SHIFT (0U) +#define PERIPH_MUX_WIDTH (3U) +#define PERIPH_DIV1_SHIFT (8U) +#define PERIPH_DIV1_WIDTH (6U) +#define PERIPH_DIV2_SHIFT (16U) +#define PERIPH_DIV2_WIDTH (6U) +#define PERIPH_GATE_SHIFT (24U) +#define PERIPH_GATE_WIDTH (1U) + +#define USB_GATE_SHIFT (25U) + +/* External clock related definitions */ + +#define EXT_CLK_MIO_DATA(mio) \ + [EXT_CLK_INDEX(EXT_CLK_MIO##mio)] = { \ + .name = "mio_clk_"#mio, \ + } + +#define EXT_CLK_INDEX(n) (n - CLK_MAX_OUTPUT_CLK) + +/* Clock control related definitions */ +#define BIT_MASK(x, y) (((1U << (y)) - 1) << (x)) + +#define ISPLL(id) (id == CLK_APLL_INT || \ + id == CLK_DPLL_INT || \ + id == CLK_VPLL_INT || \ + id == CLK_IOPLL_INT || \ + id == CLK_RPLL_INT) + + +#define PLLCTRL_BP_MASK BIT(3) +#define PLLCTRL_RESET_MASK (1U) +#define PLL_FRAC_OFFSET (8U) +#define PLL_FRAC_MODE (1U) +#define PLL_INT_MODE (0U) +#define PLL_FRAC_MODE_MASK (0x80000000U) +#define PLL_FRAC_MODE_SHIFT (31U) +#define PLL_FRAC_DATA_MASK (0xFFFFU) +#define PLL_FRAC_DATA_SHIFT (0U) +#define PLL_FBDIV_MASK (0x7F00U) +#define PLL_FBDIV_WIDTH (7U) +#define PLL_FBDIV_SHIFT (8U) + +#define CLK_PLL_RESET_ASSERT (1U) +#define CLK_PLL_RESET_RELEASE (2U) +#define CLK_PLL_RESET_PULSE (CLK_PLL_RESET_ASSERT | CLK_PLL_RESET_RELEASE) + +/* Common topology definitions */ +#define GENERIC_MUX \ + { \ + .type = TYPE_MUX, \ + .offset = PERIPH_MUX_SHIFT, \ + .width = PERIPH_MUX_WIDTH, \ + .clkflags = CLK_SET_RATE_NO_REPARENT | \ + CLK_IS_BASIC, \ + .typeflags = NA_TYPE_FLAGS, \ + .mult = NA_MULT, \ + .div = NA_DIV, \ + } + +#define IGNORE_UNUSED_MUX \ + { \ + .type = TYPE_MUX, \ + .offset = PERIPH_MUX_SHIFT, \ + .width = PERIPH_MUX_WIDTH, \ + .clkflags = CLK_IGNORE_UNUSED | \ + CLK_SET_RATE_NO_REPARENT | \ + CLK_IS_BASIC, \ + .typeflags = NA_TYPE_FLAGS, \ + .mult = NA_MULT, \ + .div = NA_DIV, \ + } + +#define GENERIC_DIV1 \ + { \ + .type = TYPE_DIV1, \ + .offset = PERIPH_DIV1_SHIFT, \ + .width = PERIPH_DIV1_WIDTH, \ + .clkflags = CLK_SET_RATE_NO_REPARENT | \ + CLK_IS_BASIC, \ + .typeflags = CLK_DIVIDER_ONE_BASED | \ + CLK_DIVIDER_ALLOW_ZERO, \ + .mult = NA_MULT, \ + .div = NA_DIV, \ + } + +#define GENERIC_DIV2 \ + { \ + .type = TYPE_DIV2, \ + .offset = PERIPH_DIV2_SHIFT, \ + .width = PERIPH_DIV2_WIDTH, \ + .clkflags = CLK_SET_RATE_NO_REPARENT | \ + CLK_SET_RATE_PARENT | \ + CLK_IS_BASIC, \ + .typeflags = CLK_DIVIDER_ONE_BASED | \ + CLK_DIVIDER_ALLOW_ZERO, \ + .mult = NA_MULT, \ + .div = NA_DIV, \ + } + +#define IGNORE_UNUSED_DIV(id) \ + { \ + .type = TYPE_DIV##id, \ + .offset = PERIPH_DIV##id##_SHIFT, \ + .width = PERIPH_DIV##id##_WIDTH, \ + .clkflags = CLK_IGNORE_UNUSED | \ + CLK_SET_RATE_NO_REPARENT | \ + CLK_IS_BASIC, \ + .typeflags = CLK_DIVIDER_ONE_BASED | \ + CLK_DIVIDER_ALLOW_ZERO, \ + .mult = NA_MULT, \ + .div = NA_DIV, \ + } + +#define GENERIC_GATE \ + { \ + .type = TYPE_GATE, \ + .offset = PERIPH_GATE_SHIFT, \ + .width = PERIPH_GATE_WIDTH, \ + .clkflags = CLK_SET_RATE_PARENT | \ + CLK_SET_RATE_GATE | \ + CLK_IS_BASIC, \ + .typeflags = NA_TYPE_FLAGS, \ + .mult = NA_MULT, \ + .div = NA_DIV, \ + } + +#define IGNORE_UNUSED_GATE \ + { \ + .type = TYPE_GATE, \ + .offset = PERIPH_GATE_SHIFT, \ + .width = PERIPH_GATE_WIDTH, \ + .clkflags = CLK_SET_RATE_PARENT | \ + CLK_IGNORE_UNUSED | \ + CLK_IS_BASIC, \ + .typeflags = NA_TYPE_FLAGS, \ + .mult = NA_MULT, \ + .div = NA_DIV, \ + } + +/** + * struct pm_clock_node - Clock topology node information. + * @type: Topology type (mux/div1/div2/gate/pll/fixed factor). + * @offset: Offset in control register. + * @width: Width of the specific type in control register. + * @clkflags: Clk specific flags. + * @typeflags: Type specific flags. + * @mult: Multiplier for fixed factor. + * @div: Divisor for fixed factor. + * + */ +struct pm_clock_node { + uint16_t clkflags; + uint16_t typeflags; + uint8_t type; + uint8_t offset; + uint8_t width; + uint8_t mult:4; + uint8_t div:4; +}; + +/** + * struct pm_clock - Clock structure. + * @name: Clock name. + * @num_nodes: number of nodes. + * @control_reg: Control register address. + * @status_reg: Status register address. + * @parents: Parents for first clock node. Lower byte indicates parent + * clock id and upper byte indicate flags for that id. + * @nodes: Clock nodes. + * + */ +struct pm_clock { + char name[CLK_NAME_LEN]; + uint8_t num_nodes; + uint32_t control_reg; + uint32_t status_reg; + int32_t (*parents)[]; + struct pm_clock_node(*nodes)[]; +}; + +/** + * struct pm_ext_clock - Clock structure. + * @name: Clock name. + * + */ +struct pm_ext_clock { + char name[CLK_NAME_LEN]; +}; + +/* PLL Clocks */ +static struct pm_clock_node generic_pll_nodes[] = { + { + .type = TYPE_PLL, + .offset = NA_SHIFT, + .width = NA_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node ignore_unused_pll_nodes[] = { + { + .type = TYPE_PLL, + .offset = NA_SHIFT, + .width = NA_WIDTH, + .clkflags = CLK_IGNORE_UNUSED | CLK_SET_RATE_NO_REPARENT, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node generic_pll_pre_src_nodes[] = { + { + .type = TYPE_MUX, + .offset = PLL_PRESRC_MUX_SHIFT, + .width = PLL_PRESRC_MUX_WIDTH, + .clkflags = CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node generic_pll_half_nodes[] = { + { + .type = TYPE_FIXEDFACTOR, + .offset = NA_SHIFT, + .width = NA_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, + .typeflags = NA_TYPE_FLAGS, + .mult = 1, + .div = 2, + }, +}; + +static struct pm_clock_node generic_pll_int_nodes[] = { + { + .type = TYPE_MUX, + .offset = PLL_DIV2_MUX_SHIFT, + .width = PLL_DIV2_MUX_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT | + CLK_SET_RATE_PARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node generic_pll_post_src_nodes[] = { + { + .type = TYPE_MUX, + .offset = PLL_POSTSRC_MUX_SHIFT, + .width = PLL_POSTSRC_MUX_WIDTH, + .clkflags = CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node generic_pll_system_nodes[] = { + { + .type = TYPE_MUX, + .offset = PLL_BYPASS_MUX_SHIFT, + .width = PLL_BYPASS_MUX_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT | + CLK_SET_RATE_PARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node acpu_nodes[] = { + { + .type = TYPE_MUX, + .offset = PERIPH_MUX_SHIFT, + .width = PERIPH_MUX_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, + { + .type = TYPE_DIV1, + .offset = PERIPH_DIV1_SHIFT, + .width = PERIPH_DIV1_WIDTH, + .clkflags = CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node generic_mux_div_nodes[] = { + GENERIC_MUX, + GENERIC_DIV1, +}; + +static struct pm_clock_node generic_mux_div_gate_nodes[] = { + GENERIC_MUX, + GENERIC_DIV1, + GENERIC_GATE, +}; + +static struct pm_clock_node generic_mux_div_unused_gate_nodes[] = { + GENERIC_MUX, + GENERIC_DIV1, + IGNORE_UNUSED_GATE, +}; + +static struct pm_clock_node generic_mux_div_div_gate_nodes[] = { + GENERIC_MUX, + GENERIC_DIV1, + GENERIC_DIV2, + GENERIC_GATE, +}; + +static struct pm_clock_node dp_audio_video_ref_nodes[] = { + { + .type = TYPE_MUX, + .offset = PERIPH_MUX_SHIFT, + .width = PERIPH_MUX_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT | + CLK_SET_RATE_PARENT | CLK_IS_BASIC, + .typeflags = CLK_FRAC, + .mult = NA_MULT, + .div = NA_DIV, + }, + { + .type = TYPE_DIV1, + .offset = PERIPH_DIV1_SHIFT, + .width = PERIPH_DIV1_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT | + CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_FRAC, + .mult = NA_MULT, + .div = NA_DIV, + }, + { + .type = TYPE_DIV2, + .offset = PERIPH_DIV2_SHIFT, + .width = PERIPH_DIV2_WIDTH, + .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT | + CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_FRAC, + .mult = NA_MULT, + .div = NA_DIV, + }, + { + .type = TYPE_GATE, + .offset = PERIPH_GATE_SHIFT, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_GATE | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node usb_nodes[] = { + GENERIC_MUX, + GENERIC_DIV1, + GENERIC_DIV2, + { + .type = TYPE_GATE, + .offset = USB_GATE_SHIFT, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_SET_RATE_PARENT | CLK_IS_BASIC | + CLK_SET_RATE_GATE, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node generic_domain_crossing_nodes[] = { + { + .type = TYPE_DIV1, + .offset = 8, + .width = 6, + .clkflags = CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node rpll_to_fpd_nodes[] = { + { + .type = TYPE_DIV1, + .offset = 8, + .width = 6, + .clkflags = CLK_SET_RATE_PARENT | CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node acpu_half_nodes[] = { + { + .type = TYPE_FIXEDFACTOR, + .offset = 0, + .width = 1, + .clkflags = 0, + .typeflags = 0, + .mult = 1, + .div = 2, + }, + { + .type = TYPE_GATE, + .offset = 25, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node acpu_full_nodes[] = { + { + .type = TYPE_GATE, + .offset = 24, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_IGNORE_UNUSED | + CLK_SET_RATE_PARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node wdt_nodes[] = { + { + .type = TYPE_MUX, + .offset = 0, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node ddr_nodes[] = { + GENERIC_MUX, + { + .type = TYPE_DIV1, + .offset = 8, + .width = 6, + .clkflags = CLK_IS_BASIC | CLK_IS_CRITICAL, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node pl_nodes[] = { + GENERIC_MUX, + { + .type = TYPE_DIV1, + .offset = PERIPH_DIV1_SHIFT, + .width = PERIPH_DIV1_WIDTH, + .clkflags = CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, + { + .type = TYPE_DIV2, + .offset = PERIPH_DIV2_SHIFT, + .width = PERIPH_DIV2_WIDTH, + .clkflags = CLK_IS_BASIC | CLK_SET_RATE_PARENT, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, + { + .type = TYPE_GATE, + .offset = PERIPH_GATE_SHIFT, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_SET_RATE_PARENT | CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gpu_pp0_nodes[] = { + { + .type = TYPE_GATE, + .offset = 25, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_SET_RATE_PARENT | CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gpu_pp1_nodes[] = { + { + .type = TYPE_GATE, + .offset = 26, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_SET_RATE_PARENT | CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem_ref_ungated_nodes[] = { + GENERIC_MUX, + { + .type = TYPE_DIV1, + .offset = 8, + .width = 6, + .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, + { + .type = TYPE_DIV2, + .offset = 16, + .width = 6, + .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_IS_BASIC | + CLK_SET_RATE_PARENT, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem0_ref_nodes[] = { + { + .type = TYPE_MUX, + .offset = 1, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem1_ref_nodes[] = { + { + .type = TYPE_MUX, + .offset = 6, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem2_ref_nodes[] = { + { + .type = TYPE_MUX, + .offset = 11, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem3_ref_nodes[] = { + { + .type = TYPE_MUX, + .offset = 16, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem_tx_nodes[] = { + { + .type = TYPE_GATE, + .offset = 25, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_SET_RATE_PARENT | CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem_rx_nodes[] = { + { + .type = TYPE_GATE, + .offset = 26, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node gem_tsu_nodes[] = { + { + .type = TYPE_MUX, + .offset = 20, + .width = 2, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node can0_mio_nodes[] = { + { + .type = TYPE_MUX, + .offset = 0, + .width = 7, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node can1_mio_nodes[] = { + { + .type = TYPE_MUX, + .offset = 15, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node can0_nodes[] = { + { + .type = TYPE_MUX, + .offset = 7, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node can1_nodes[] = { + { + .type = TYPE_MUX, + .offset = 22, + .width = 1, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node cpu_r5_core_nodes[] = { + { + .type = TYPE_GATE, + .offset = 25, + .width = PERIPH_GATE_WIDTH, + .clkflags = CLK_IGNORE_UNUSED | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node dll_ref_nodes[] = { + { + .type = TYPE_MUX, + .offset = 0, + .width = 3, + .clkflags = CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT | + CLK_IS_BASIC, + .typeflags = NA_TYPE_FLAGS, + .mult = NA_MULT, + .div = NA_DIV, + }, +}; + +static struct pm_clock_node timestamp_ref_nodes[] = { + GENERIC_MUX, + { + .type = TYPE_DIV1, + .offset = 8, + .width = 6, + .clkflags = CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + .mult = NA_MULT, + .div = NA_DIV, + }, + IGNORE_UNUSED_GATE, +}; + +static int32_t can_mio_parents[] = { + EXT_CLK_MIO0, EXT_CLK_MIO1, EXT_CLK_MIO2, EXT_CLK_MIO3, + EXT_CLK_MIO4, EXT_CLK_MIO5, EXT_CLK_MIO6, EXT_CLK_MIO7, + EXT_CLK_MIO8, EXT_CLK_MIO9, EXT_CLK_MIO10, EXT_CLK_MIO11, + EXT_CLK_MIO12, EXT_CLK_MIO13, EXT_CLK_MIO14, EXT_CLK_MIO15, + EXT_CLK_MIO16, EXT_CLK_MIO17, EXT_CLK_MIO18, EXT_CLK_MIO19, + EXT_CLK_MIO20, EXT_CLK_MIO21, EXT_CLK_MIO22, EXT_CLK_MIO23, + EXT_CLK_MIO24, EXT_CLK_MIO25, EXT_CLK_MIO26, EXT_CLK_MIO27, + EXT_CLK_MIO28, EXT_CLK_MIO29, EXT_CLK_MIO30, EXT_CLK_MIO31, + EXT_CLK_MIO32, EXT_CLK_MIO33, EXT_CLK_MIO34, EXT_CLK_MIO35, + EXT_CLK_MIO36, EXT_CLK_MIO37, EXT_CLK_MIO38, EXT_CLK_MIO39, + EXT_CLK_MIO40, EXT_CLK_MIO41, EXT_CLK_MIO42, EXT_CLK_MIO43, + EXT_CLK_MIO44, EXT_CLK_MIO45, EXT_CLK_MIO46, EXT_CLK_MIO47, + EXT_CLK_MIO48, EXT_CLK_MIO49, EXT_CLK_MIO50, EXT_CLK_MIO51, + EXT_CLK_MIO52, EXT_CLK_MIO53, EXT_CLK_MIO54, EXT_CLK_MIO55, + EXT_CLK_MIO56, EXT_CLK_MIO57, EXT_CLK_MIO58, EXT_CLK_MIO59, + EXT_CLK_MIO60, EXT_CLK_MIO61, EXT_CLK_MIO62, EXT_CLK_MIO63, + EXT_CLK_MIO64, EXT_CLK_MIO65, EXT_CLK_MIO66, EXT_CLK_MIO67, + EXT_CLK_MIO68, EXT_CLK_MIO69, EXT_CLK_MIO70, EXT_CLK_MIO71, + EXT_CLK_MIO72, EXT_CLK_MIO73, EXT_CLK_MIO74, EXT_CLK_MIO75, + EXT_CLK_MIO76, EXT_CLK_MIO77, CLK_NA_PARENT +}; + +/* Clock array containing clock informaton */ +static struct pm_clock clocks[] = { + [CLK_APLL_INT] = { + .name = "apll_int", + .control_reg = CRF_APB_APLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_APLL_PRE_SRC, CLK_NA_PARENT}), + .nodes = &ignore_unused_pll_nodes, + .num_nodes = ARRAY_SIZE(ignore_unused_pll_nodes), + }, + [CLK_APLL_PRE_SRC] = { + .name = "apll_pre_src", + .control_reg = CRF_APB_APLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_pre_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_pre_src_nodes), + }, + [CLK_APLL_HALF] = { + .name = "apll_half", + .control_reg = CRF_APB_APLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_APLL_INT, CLK_NA_PARENT}), + .nodes = &generic_pll_half_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_half_nodes), + }, + [CLK_APLL_INT_MUX] = { + .name = "apll_int_mux", + .control_reg = CRF_APB_APLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_APLL_INT, + CLK_APLL_HALF, + CLK_NA_PARENT + }), + .nodes = &generic_pll_int_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_int_nodes), + }, + [CLK_APLL_POST_SRC] = { + .name = "apll_post_src", + .control_reg = CRF_APB_APLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_post_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_post_src_nodes), + }, + [CLK_APLL] = { + .name = "apll", + .control_reg = CRF_APB_APLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_APLL_INT_MUX, + CLK_APLL_POST_SRC, + CLK_NA_PARENT + }), + .nodes = &generic_pll_system_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_system_nodes), + }, + [CLK_DPLL_INT] = { + .name = "dpll_int", + .control_reg = CRF_APB_DPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_DPLL_PRE_SRC, CLK_NA_PARENT}), + .nodes = &generic_pll_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_nodes), + }, + [CLK_DPLL_PRE_SRC] = { + .name = "dpll_pre_src", + .control_reg = CRF_APB_DPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_pre_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_pre_src_nodes), + }, + [CLK_DPLL_HALF] = { + .name = "dpll_half", + .control_reg = CRF_APB_DPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_DPLL_INT, CLK_NA_PARENT}), + .nodes = &generic_pll_half_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_half_nodes), + }, + [CLK_DPLL_INT_MUX] = { + .name = "dpll_int_mux", + .control_reg = CRF_APB_DPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_DPLL_INT, + CLK_DPLL_HALF, + CLK_NA_PARENT + }), + .nodes = &generic_pll_int_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_int_nodes), + }, + [CLK_DPLL_POST_SRC] = { + .name = "dpll_post_src", + .control_reg = CRF_APB_DPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_post_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_post_src_nodes), + }, + [CLK_DPLL] = { + .name = "dpll", + .control_reg = CRF_APB_DPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_DPLL_INT_MUX, + CLK_DPLL_POST_SRC, + CLK_NA_PARENT + }), + .nodes = &generic_pll_system_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_system_nodes), + }, + [CLK_VPLL_INT] = { + .name = "vpll_int", + .control_reg = CRF_APB_VPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_VPLL_PRE_SRC, CLK_NA_PARENT}), + .nodes = &ignore_unused_pll_nodes, + .num_nodes = ARRAY_SIZE(ignore_unused_pll_nodes), + }, + [CLK_VPLL_PRE_SRC] = { + .name = "vpll_pre_src", + .control_reg = CRF_APB_VPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_pre_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_pre_src_nodes), + }, + [CLK_VPLL_HALF] = { + .name = "vpll_half", + .control_reg = CRF_APB_VPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_VPLL_INT, CLK_NA_PARENT}), + .nodes = &generic_pll_half_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_half_nodes), + }, + [CLK_VPLL_INT_MUX] = { + .name = "vpll_int_mux", + .control_reg = CRF_APB_VPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_VPLL_INT, + CLK_VPLL_HALF, + CLK_NA_PARENT + }), + .nodes = &generic_pll_int_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_int_nodes), + }, + [CLK_VPLL_POST_SRC] = { + .name = "vpll_post_src", + .control_reg = CRF_APB_VPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_post_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_post_src_nodes), + }, + [CLK_VPLL] = { + .name = "vpll", + .control_reg = CRF_APB_VPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_VPLL_INT_MUX, + CLK_VPLL_POST_SRC, + CLK_NA_PARENT + }), + .nodes = &generic_pll_system_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_system_nodes), + }, + [CLK_IOPLL_INT] = { + .name = "iopll_int", + .control_reg = CRL_APB_IOPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_IOPLL_PRE_SRC, CLK_NA_PARENT}), + .nodes = &generic_pll_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_nodes), + }, + [CLK_IOPLL_PRE_SRC] = { + .name = "iopll_pre_src", + .control_reg = CRL_APB_IOPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_pre_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_pre_src_nodes), + }, + [CLK_IOPLL_HALF] = { + .name = "iopll_half", + .control_reg = CRL_APB_IOPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_IOPLL_INT, CLK_NA_PARENT}), + .nodes = &generic_pll_half_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_half_nodes), + }, + [CLK_IOPLL_INT_MUX] = { + .name = "iopll_int_mux", + .control_reg = CRL_APB_IOPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_IOPLL_INT, + CLK_IOPLL_HALF, + CLK_NA_PARENT + }), + .nodes = &generic_pll_int_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_int_nodes), + }, + [CLK_IOPLL_POST_SRC] = { + .name = "iopll_post_src", + .control_reg = CRL_APB_IOPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_post_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_post_src_nodes), + }, + [CLK_IOPLL] = { + .name = "iopll", + .control_reg = CRL_APB_IOPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_IOPLL_INT_MUX, + CLK_IOPLL_POST_SRC, + CLK_NA_PARENT + }), + .nodes = &generic_pll_system_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_system_nodes), + }, + [CLK_RPLL_INT] = { + .name = "rpll_int", + .control_reg = CRL_APB_RPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_RPLL_PRE_SRC, CLK_NA_PARENT}), + .nodes = &generic_pll_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_nodes), + }, + [CLK_RPLL_PRE_SRC] = { + .name = "rpll_pre_src", + .control_reg = CRL_APB_RPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + + .nodes = &generic_pll_pre_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_pre_src_nodes), + }, + [CLK_RPLL_HALF] = { + .name = "rpll_half", + .control_reg = CRL_APB_RPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) {CLK_RPLL_INT, CLK_NA_PARENT}), + .nodes = &generic_pll_half_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_half_nodes), + }, + [CLK_RPLL_INT_MUX] = { + .name = "rpll_int_mux", + .control_reg = CRL_APB_RPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_RPLL_INT, + CLK_RPLL_HALF, + CLK_NA_PARENT + }), + .nodes = &generic_pll_int_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_int_nodes), + }, + [CLK_RPLL_POST_SRC] = { + .name = "rpll_post_src", + .control_reg = CRL_APB_RPLL_CTRL, + .status_reg = CRF_APB_PLL_STATUS, + .parents = &((int32_t []) { + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_VIDEO | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_ALT_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_AUX_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_GT_CRX_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &generic_pll_post_src_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_post_src_nodes), + }, + [CLK_RPLL] = { + .name = "rpll", + .control_reg = CRL_APB_RPLL_CTRL, + .status_reg = CRL_APB_PLL_STATUS, + .parents = &((int32_t []) { + CLK_RPLL_INT_MUX, + CLK_RPLL_POST_SRC, + CLK_NA_PARENT + }), + .nodes = &generic_pll_system_nodes, + .num_nodes = ARRAY_SIZE(generic_pll_system_nodes), + }, + /* Peripheral Clocks */ + [CLK_ACPU] = { + .name = "acpu", + .control_reg = CRF_APB_ACPU_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_APLL, + CLK_DUMMY_PARENT, + CLK_DPLL, + CLK_VPLL, + CLK_NA_PARENT + }), + .nodes = &acpu_nodes, + .num_nodes = ARRAY_SIZE(acpu_nodes), + }, + [CLK_ACPU_FULL] = { + .name = "acpu_full", + .control_reg = CRF_APB_ACPU_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_ACPU | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + CLK_NA_PARENT + }), + .nodes = &acpu_full_nodes, + .num_nodes = ARRAY_SIZE(acpu_full_nodes), + }, + [CLK_DBG_TRACE] = { + .name = "dbg_trace", + .control_reg = CRF_APB_DBG_TRACE_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL_TO_FPD, + CLK_DUMMY_PARENT, + CLK_DPLL, + CLK_APLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_DBG_FPD] = { + .name = "dbg_fpd", + .control_reg = CRF_APB_DBG_FPD_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL_TO_FPD, + CLK_DUMMY_PARENT, + CLK_DPLL, + CLK_APLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_DBG_TSTMP] = { + .name = "dbg_tstmp", + .control_reg = CRF_APB_DBG_TSTMP_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL_TO_FPD, + CLK_DUMMY_PARENT, + CLK_DPLL, + CLK_APLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_nodes), + }, + [CLK_DP_VIDEO_REF] = { + .name = "dp_video_ref", + .control_reg = CRF_APB_DP_VIDEO_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_VPLL, + CLK_DUMMY_PARENT, + CLK_DPLL, + CLK_RPLL_TO_FPD, + CLK_NA_PARENT + }), + .nodes = &dp_audio_video_ref_nodes, + .num_nodes = ARRAY_SIZE(dp_audio_video_ref_nodes), + }, + [CLK_DP_AUDIO_REF] = { + .name = "dp_audio_ref", + .control_reg = CRF_APB_DP_AUDIO_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_VPLL, + CLK_DUMMY_PARENT, + CLK_DPLL, + CLK_RPLL_TO_FPD, + CLK_NA_PARENT + }), + .nodes = &dp_audio_video_ref_nodes, + .num_nodes = ARRAY_SIZE(dp_audio_video_ref_nodes), + }, + [CLK_DP_STC_REF] = { + .name = "dp_stc_ref", + .control_reg = CRF_APB_DP_STC_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_VPLL, + CLK_DUMMY_PARENT, + CLK_DPLL, + CLK_RPLL_TO_FPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_DPDMA_REF] = { + .name = "dpdma_ref", + .control_reg = CRF_APB_DPDMA_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_APLL, + CLK_DUMMY_PARENT, + CLK_VPLL, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_DDR_REF] = { + .name = "ddr_ref", + .control_reg = CRF_APB_DDR_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_DPLL, + CLK_VPLL, + CLK_NA_PARENT + }), + .nodes = &ddr_nodes, + .num_nodes = ARRAY_SIZE(ddr_nodes), + }, + [CLK_GPU_REF] = { + .name = "gpu_ref", + .control_reg = CRF_APB_GPU_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL_TO_FPD, + CLK_DUMMY_PARENT, + CLK_VPLL, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_SATA_REF] = { + .name = "sata_ref", + .control_reg = CRF_APB_SATA_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL_TO_FPD, + CLK_DUMMY_PARENT, + CLK_APLL, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_PCIE_REF] = { + .name = "pcie_ref", + .control_reg = CRF_APB_PCIE_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL_TO_FPD, + CLK_DUMMY_PARENT, + CLK_RPLL_TO_FPD, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_GDMA_REF] = { + .name = "gdma_ref", + .control_reg = CRF_APB_GDMA_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_APLL, + CLK_DUMMY_PARENT, + CLK_VPLL, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_GTGREF0_REF] = { + .name = "gtgref0_ref", + .control_reg = CRF_APB_GTGREF0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL_TO_FPD, + CLK_DUMMY_PARENT, + CLK_APLL, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_TOPSW_MAIN] = { + .name = "topsw_main", + .control_reg = CRF_APB_TOPSW_MAIN_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_APLL, + CLK_DUMMY_PARENT, + CLK_VPLL, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_unused_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_unused_gate_nodes), + }, + [CLK_TOPSW_LSBUS] = { + .name = "topsw_lsbus", + .control_reg = CRF_APB_TOPSW_LSBUS_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_APLL, + CLK_DUMMY_PARENT, + CLK_IOPLL_TO_FPD, + CLK_DPLL, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_unused_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_unused_gate_nodes), + }, + [CLK_IOU_SWITCH] = { + .name = "iou_switch", + .control_reg = CRL_APB_IOU_SWITCH_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_RPLL, + CLK_DUMMY_PARENT, + CLK_IOPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_unused_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_unused_gate_nodes), + }, + [CLK_GEM0_REF_UNGATED] = { + .name = "gem0_ref_ung", + .control_reg = CRL_APB_GEM0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &gem_ref_ungated_nodes, + .num_nodes = ARRAY_SIZE(gem_ref_ungated_nodes), + }, + [CLK_GEM1_REF_UNGATED] = { + .name = "gem1_ref_ung", + .control_reg = CRL_APB_GEM1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &gem_ref_ungated_nodes, + .num_nodes = ARRAY_SIZE(gem_ref_ungated_nodes), + }, + [CLK_GEM2_REF_UNGATED] = { + .name = "gem2_ref_ung", + .control_reg = CRL_APB_GEM2_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &gem_ref_ungated_nodes, + .num_nodes = ARRAY_SIZE(gem_ref_ungated_nodes), + }, + [CLK_GEM3_REF_UNGATED] = { + .name = "gem3_ref_ung", + .control_reg = CRL_APB_GEM3_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &gem_ref_ungated_nodes, + .num_nodes = ARRAY_SIZE(gem_ref_ungated_nodes), + }, + [CLK_GEM0_REF] = { + .name = "gem0_ref", + .control_reg = IOU_SLCR_GEM_CLK_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM0_REF_UNGATED | + (PARENT_CLK_NODE3 << CLK_PARENTS_ID_LEN), + EXT_CLK_GEM0_TX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem0_ref_nodes, + .num_nodes = ARRAY_SIZE(gem0_ref_nodes), + }, + [CLK_GEM1_REF] = { + .name = "gem1_ref", + .control_reg = IOU_SLCR_GEM_CLK_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM1_REF_UNGATED | + (PARENT_CLK_NODE3 << CLK_PARENTS_ID_LEN), + EXT_CLK_GEM1_TX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem1_ref_nodes, + .num_nodes = ARRAY_SIZE(gem1_ref_nodes), + }, + [CLK_GEM2_REF] = { + .name = "gem2_ref", + .control_reg = IOU_SLCR_GEM_CLK_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM2_REF_UNGATED | + (PARENT_CLK_NODE3 << CLK_PARENTS_ID_LEN), + EXT_CLK_GEM2_TX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem2_ref_nodes, + .num_nodes = ARRAY_SIZE(gem2_ref_nodes), + }, + [CLK_GEM3_REF] = { + .name = "gem3_ref", + .control_reg = IOU_SLCR_GEM_CLK_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM3_REF_UNGATED | + (PARENT_CLK_NODE3 << CLK_PARENTS_ID_LEN), + EXT_CLK_GEM3_TX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem3_ref_nodes, + .num_nodes = ARRAY_SIZE(gem3_ref_nodes), + }, + [CLK_USB0_BUS_REF] = { + .name = "usb0_bus_ref", + .control_reg = CRL_APB_USB0_BUS_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &usb_nodes, + .num_nodes = ARRAY_SIZE(usb_nodes), + }, + [CLK_USB1_BUS_REF] = { + .name = "usb1_bus_ref", + .control_reg = CRL_APB_USB1_BUS_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &usb_nodes, + .num_nodes = ARRAY_SIZE(usb_nodes), + }, + [CLK_USB3_DUAL_REF] = { + .name = "usb3_dual_ref", + .control_reg = CRL_APB_USB3_DUAL_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &usb_nodes, + .num_nodes = ARRAY_SIZE(usb_nodes), + }, + [CLK_QSPI_REF] = { + .name = "qspi_ref", + .control_reg = CRL_APB_QSPI_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_SDIO0_REF] = { + .name = "sdio0_ref", + .control_reg = CRL_APB_SDIO0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_VPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_SDIO1_REF] = { + .name = "sdio1_ref", + .control_reg = CRL_APB_SDIO1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_VPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_UART0_REF] = { + .name = "uart0_ref", + .control_reg = CRL_APB_UART0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_UART1_REF] = { + .name = "uart1_ref", + .control_reg = CRL_APB_UART1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_SPI0_REF] = { + .name = "spi0_ref", + .control_reg = CRL_APB_SPI0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_SPI1_REF] = { + .name = "spi1_ref", + .control_reg = CRL_APB_SPI1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_CAN0_REF] = { + .name = "can0_ref", + .control_reg = CRL_APB_CAN0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_CAN1_REF] = { + .name = "can1_ref", + .control_reg = CRL_APB_CAN1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_NAND_REF] = { + .name = "nand_ref", + .control_reg = CRL_APB_NAND_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_GEM_TSU_REF] = { + .name = "gem_tsu_ref", + .control_reg = CRL_APB_GEM_TSU_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_DLL_REF] = { + .name = "dll_ref", + .control_reg = CRL_APB_DLL_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_RPLL, + CLK_NA_PARENT + }), + .nodes = &dll_ref_nodes, + .num_nodes = ARRAY_SIZE(dll_ref_nodes), + }, + [CLK_ADMA_REF] = { + .name = "adma_ref", + .control_reg = CRL_APB_ADMA_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_RPLL, + CLK_DUMMY_PARENT, + CLK_IOPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_DBG_LPD] = { + .name = "dbg_lpd", + .control_reg = CRL_APB_DBG_LPD_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_RPLL, + CLK_DUMMY_PARENT, + CLK_IOPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_CPU_R5] = { + .name = "cpu_r5", + .control_reg = CRL_APB_CPU_R5_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_RPLL, + CLK_DUMMY_PARENT, + CLK_IOPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_unused_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_unused_gate_nodes), + }, + [CLK_CSU_PLL] = { + .name = "csu_pll", + .control_reg = CRL_APB_CSU_PLL_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_PCAP] = { + .name = "pcap", + .control_reg = CRL_APB_PCAP_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_gate_nodes), + }, + [CLK_LPD_LSBUS] = { + .name = "lpd_lsbus", + .control_reg = CRL_APB_LPD_LSBUS_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_RPLL, + CLK_DUMMY_PARENT, + CLK_IOPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_unused_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_unused_gate_nodes), + }, + [CLK_LPD_SWITCH] = { + .name = "lpd_switch", + .control_reg = CRL_APB_LPD_SWITCH_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_RPLL, + CLK_DUMMY_PARENT, + CLK_IOPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_unused_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_unused_gate_nodes), + }, + [CLK_I2C0_REF] = { + .name = "i2c0_ref", + .control_reg = CRL_APB_I2C0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_I2C1_REF] = { + .name = "i2c1_ref", + .control_reg = CRL_APB_I2C1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_TIMESTAMP_REF] = { + .name = "timestamp_ref", + .control_reg = CRL_APB_TIMESTAMP_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + EXT_CLK_PSS_REF | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = ×tamp_ref_nodes, + .num_nodes = ARRAY_SIZE(timestamp_ref_nodes), + }, + [CLK_PL0_REF] = { + .name = "pl0_ref", + .control_reg = CRL_APB_PL0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &pl_nodes, + .num_nodes = ARRAY_SIZE(pl_nodes), + }, + [CLK_PL1_REF] = { + .name = "pl1_ref", + .control_reg = CRL_APB_PL1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &pl_nodes, + .num_nodes = ARRAY_SIZE(pl_nodes), + }, + [CLK_PL2_REF] = { + .name = "pl2_ref", + .control_reg = CRL_APB_PL2_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &pl_nodes, + .num_nodes = ARRAY_SIZE(pl_nodes), + }, + [CLK_PL3_REF] = { + .name = "pl3_ref", + .control_reg = CRL_APB_PL3_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_IOPLL, + CLK_DUMMY_PARENT, + CLK_RPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &pl_nodes, + .num_nodes = ARRAY_SIZE(pl_nodes), + }, + [CLK_AMS_REF] = { + .name = "ams_ref", + .control_reg = CRL_APB_AMS_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_RPLL, + CLK_DUMMY_PARENT, + CLK_IOPLL, + CLK_DPLL_TO_LPD, + CLK_NA_PARENT + }), + .nodes = &generic_mux_div_div_gate_nodes, + .num_nodes = ARRAY_SIZE(generic_mux_div_div_gate_nodes), + }, + [CLK_IOPLL_TO_FPD] = { + .name = "iopll_to_fpd", + .control_reg = CRL_APB_IOPLL_TO_FPD_CTRL, + .status_reg = 0, + .parents = &((int32_t []) {CLK_IOPLL, CLK_NA_PARENT}), + .nodes = &generic_domain_crossing_nodes, + .num_nodes = ARRAY_SIZE(generic_domain_crossing_nodes), + }, + [CLK_RPLL_TO_FPD] = { + .name = "rpll_to_fpd", + .control_reg = CRL_APB_RPLL_TO_FPD_CTRL, + .status_reg = 0, + .parents = &((int32_t []) {CLK_RPLL, CLK_NA_PARENT}), + .nodes = &rpll_to_fpd_nodes, + .num_nodes = ARRAY_SIZE(rpll_to_fpd_nodes), + }, + [CLK_APLL_TO_LPD] = { + .name = "apll_to_lpd", + .control_reg = CRF_APB_APLL_TO_LPD_CTRL, + .status_reg = 0, + .parents = &((int32_t []) {CLK_APLL, CLK_NA_PARENT}), + .nodes = &generic_domain_crossing_nodes, + .num_nodes = ARRAY_SIZE(generic_domain_crossing_nodes), + }, + [CLK_DPLL_TO_LPD] = { + .name = "dpll_to_lpd", + .control_reg = CRF_APB_DPLL_TO_LPD_CTRL, + .status_reg = 0, + .parents = &((int32_t []) {CLK_DPLL, CLK_NA_PARENT}), + .nodes = &generic_domain_crossing_nodes, + .num_nodes = ARRAY_SIZE(generic_domain_crossing_nodes), + }, + [CLK_VPLL_TO_LPD] = { + .name = "vpll_to_lpd", + .control_reg = CRF_APB_VPLL_TO_LPD_CTRL, + .status_reg = 0, + .parents = &((int32_t []) {CLK_VPLL, CLK_NA_PARENT}), + .nodes = &generic_domain_crossing_nodes, + .num_nodes = ARRAY_SIZE(generic_domain_crossing_nodes), + }, + [CLK_GEM0_TX] = { + .name = "gem0_tx", + .control_reg = CRL_APB_GEM0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM0_REF, + CLK_NA_PARENT + }), + .nodes = &gem_tx_nodes, + .num_nodes = ARRAY_SIZE(gem_tx_nodes), + }, + [CLK_GEM1_TX] = { + .name = "gem1_tx", + .control_reg = CRL_APB_GEM1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM1_REF, + CLK_NA_PARENT + }), + .nodes = &gem_tx_nodes, + .num_nodes = ARRAY_SIZE(gem_tx_nodes), + }, + [CLK_GEM2_TX] = { + .name = "gem2_tx", + .control_reg = CRL_APB_GEM2_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM2_REF, + CLK_NA_PARENT + }), + .nodes = &gem_tx_nodes, + .num_nodes = ARRAY_SIZE(gem_tx_nodes), + }, + [CLK_GEM3_TX] = { + .name = "gem3_tx", + .control_reg = CRL_APB_GEM3_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM3_REF, + CLK_NA_PARENT + }), + .nodes = &gem_tx_nodes, + .num_nodes = ARRAY_SIZE(gem_tx_nodes), + }, + [CLK_GEM0_RX] = { + .name = "gem0_rx", + .control_reg = CRL_APB_GEM0_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + EXT_CLK_GEM0_RX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem_rx_nodes, + .num_nodes = ARRAY_SIZE(gem_rx_nodes), + }, + [CLK_GEM1_RX] = { + .name = "gem1_rx", + .control_reg = CRL_APB_GEM1_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + EXT_CLK_GEM1_RX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem_rx_nodes, + .num_nodes = ARRAY_SIZE(gem_rx_nodes), + }, + [CLK_GEM2_RX] = { + .name = "gem2_rx", + .control_reg = CRL_APB_GEM2_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + EXT_CLK_GEM2_RX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem_rx_nodes, + .num_nodes = ARRAY_SIZE(gem_rx_nodes), + }, + [CLK_GEM3_RX] = { + .name = "gem3_rx", + .control_reg = CRL_APB_GEM3_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + EXT_CLK_GEM3_RX_EMIO | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem_rx_nodes, + .num_nodes = ARRAY_SIZE(gem_rx_nodes), + }, + [CLK_ACPU_HALF] = { + .name = "acpu_half", + .control_reg = CRF_APB_ACPU_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_ACPU | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + CLK_NA_PARENT + }), + .nodes = &acpu_half_nodes, + .num_nodes = ARRAY_SIZE(acpu_half_nodes), + }, + [CLK_FPD_WDT] = { + .name = "fpd_wdt", + .control_reg = FPD_SLCR_WDT_CLK_SEL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_TOPSW_LSBUS, + EXT_CLK_SWDT0 | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &wdt_nodes, + .num_nodes = ARRAY_SIZE(wdt_nodes), + }, + [CLK_GPU_PP0_REF] = { + .name = "gpu_pp0_ref", + .control_reg = CRF_APB_GPU_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GPU_REF | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + CLK_NA_PARENT + }), + .nodes = &gpu_pp0_nodes, + .num_nodes = ARRAY_SIZE(gpu_pp0_nodes), + }, + [CLK_GPU_PP1_REF] = { + .name = "gpu_pp1_ref", + .control_reg = CRF_APB_GPU_REF_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GPU_REF | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + CLK_NA_PARENT + }), + .nodes = &gpu_pp1_nodes, + .num_nodes = ARRAY_SIZE(gpu_pp1_nodes), + }, + [CLK_GEM_TSU] = { + .name = "gem_tsu", + .control_reg = IOU_SLCR_GEM_CLK_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_GEM_TSU_REF, + CLK_GEM_TSU_REF, + EXT_CLK_MIO26 | CLK_EXTERNAL_PARENT, + EXT_CLK_MIO50_OR_MIO51 | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &gem_tsu_nodes, + .num_nodes = ARRAY_SIZE(gem_tsu_nodes), + }, + [CLK_CPU_R5_CORE] = { + .name = "cpu_r5_core", + .control_reg = CRL_APB_CPU_R5_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_CPU_R5 | PARENT_CLK_NODE2 << CLK_PARENTS_ID_LEN, + CLK_DUMMY_PARENT, + CLK_NA_PARENT + }), + .nodes = &cpu_r5_core_nodes, + .num_nodes = ARRAY_SIZE(cpu_r5_core_nodes), + }, + [CLK_CAN0_MIO] = { + .name = "can0_mio", + .control_reg = IOU_SLCR_CAN_MIO_CTRL, + .status_reg = 0, + .parents = &can_mio_parents, + .nodes = &can0_mio_nodes, + .num_nodes = ARRAY_SIZE(can0_mio_nodes), + }, + [CLK_CAN1_MIO] = { + .name = "can1_mio", + .control_reg = IOU_SLCR_CAN_MIO_CTRL, + .status_reg = 0, + .parents = &can_mio_parents, + .nodes = &can1_mio_nodes, + .num_nodes = ARRAY_SIZE(can1_mio_nodes), + }, + [CLK_CAN0] = { + .name = "can0", + .control_reg = IOU_SLCR_CAN_MIO_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_CAN0_REF, + CLK_CAN0_MIO, + CLK_NA_PARENT + }), + .nodes = &can0_nodes, + .num_nodes = ARRAY_SIZE(can0_nodes), + }, + [CLK_CAN1] = { + .name = "can1", + .control_reg = IOU_SLCR_CAN_MIO_CTRL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_CAN1_REF, + CLK_CAN1_MIO, + CLK_NA_PARENT + }), + .nodes = &can1_nodes, + .num_nodes = ARRAY_SIZE(can1_nodes), + }, + [CLK_LPD_WDT] = { + .name = "lpd_wdt", + .control_reg = IOU_SLCR_WDT_CLK_SEL, + .status_reg = 0, + .parents = &((int32_t []) { + CLK_LPD_LSBUS, + EXT_CLK_SWDT1 | CLK_EXTERNAL_PARENT, + CLK_NA_PARENT + }), + .nodes = &wdt_nodes, + .num_nodes = ARRAY_SIZE(wdt_nodes), + }, +}; + +static struct pm_ext_clock ext_clocks[] = { + [EXT_CLK_INDEX(EXT_CLK_PSS_REF)] = { + .name = "pss_ref_clk", + }, + [EXT_CLK_INDEX(EXT_CLK_VIDEO)] = { + .name = "video_clk", + }, + [EXT_CLK_INDEX(EXT_CLK_PSS_ALT_REF)] = { + .name = "pss_alt_ref_clk", + }, + [EXT_CLK_INDEX(EXT_CLK_AUX_REF)] = { + .name = "aux_ref_clk", + }, + [EXT_CLK_INDEX(EXT_CLK_GT_CRX_REF)] = { + .name = "video_clk", + }, + [EXT_CLK_INDEX(EXT_CLK_SWDT0)] = { + .name = "swdt0_ext_clk", + }, + [EXT_CLK_INDEX(EXT_CLK_SWDT1)] = { + .name = "swdt1_ext_clk", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM0_TX_EMIO)] = { + .name = "gem0_tx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM1_TX_EMIO)] = { + .name = "gem1_tx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM2_TX_EMIO)] = { + .name = "gem2_tx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM3_TX_EMIO)] = { + .name = "gem3_tx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM0_RX_EMIO)] = { + .name = "gem0_rx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM1_RX_EMIO)] = { + .name = "gem1_rx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM2_RX_EMIO)] = { + .name = "gem2_rx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_GEM3_RX_EMIO)] = { + .name = "gem3_rx_ext", + }, + [EXT_CLK_INDEX(EXT_CLK_MIO50_OR_MIO51)] = { + .name = "mio_clk_50_51", + }, + EXT_CLK_MIO_DATA(0), + EXT_CLK_MIO_DATA(1), + EXT_CLK_MIO_DATA(2), + EXT_CLK_MIO_DATA(3), + EXT_CLK_MIO_DATA(4), + EXT_CLK_MIO_DATA(5), + EXT_CLK_MIO_DATA(6), + EXT_CLK_MIO_DATA(7), + EXT_CLK_MIO_DATA(8), + EXT_CLK_MIO_DATA(9), + EXT_CLK_MIO_DATA(10), + EXT_CLK_MIO_DATA(11), + EXT_CLK_MIO_DATA(12), + EXT_CLK_MIO_DATA(13), + EXT_CLK_MIO_DATA(14), + EXT_CLK_MIO_DATA(15), + EXT_CLK_MIO_DATA(16), + EXT_CLK_MIO_DATA(17), + EXT_CLK_MIO_DATA(18), + EXT_CLK_MIO_DATA(19), + EXT_CLK_MIO_DATA(20), + EXT_CLK_MIO_DATA(21), + EXT_CLK_MIO_DATA(22), + EXT_CLK_MIO_DATA(23), + EXT_CLK_MIO_DATA(24), + EXT_CLK_MIO_DATA(25), + EXT_CLK_MIO_DATA(26), + EXT_CLK_MIO_DATA(27), + EXT_CLK_MIO_DATA(28), + EXT_CLK_MIO_DATA(29), + EXT_CLK_MIO_DATA(30), + EXT_CLK_MIO_DATA(31), + EXT_CLK_MIO_DATA(32), + EXT_CLK_MIO_DATA(33), + EXT_CLK_MIO_DATA(34), + EXT_CLK_MIO_DATA(35), + EXT_CLK_MIO_DATA(36), + EXT_CLK_MIO_DATA(37), + EXT_CLK_MIO_DATA(38), + EXT_CLK_MIO_DATA(39), + EXT_CLK_MIO_DATA(40), + EXT_CLK_MIO_DATA(41), + EXT_CLK_MIO_DATA(42), + EXT_CLK_MIO_DATA(43), + EXT_CLK_MIO_DATA(44), + EXT_CLK_MIO_DATA(45), + EXT_CLK_MIO_DATA(46), + EXT_CLK_MIO_DATA(47), + EXT_CLK_MIO_DATA(48), + EXT_CLK_MIO_DATA(49), + EXT_CLK_MIO_DATA(50), + EXT_CLK_MIO_DATA(51), + EXT_CLK_MIO_DATA(52), + EXT_CLK_MIO_DATA(53), + EXT_CLK_MIO_DATA(54), + EXT_CLK_MIO_DATA(55), + EXT_CLK_MIO_DATA(56), + EXT_CLK_MIO_DATA(57), + EXT_CLK_MIO_DATA(58), + EXT_CLK_MIO_DATA(59), + EXT_CLK_MIO_DATA(60), + EXT_CLK_MIO_DATA(61), + EXT_CLK_MIO_DATA(62), + EXT_CLK_MIO_DATA(63), + EXT_CLK_MIO_DATA(64), + EXT_CLK_MIO_DATA(65), + EXT_CLK_MIO_DATA(66), + EXT_CLK_MIO_DATA(67), + EXT_CLK_MIO_DATA(68), + EXT_CLK_MIO_DATA(69), + EXT_CLK_MIO_DATA(70), + EXT_CLK_MIO_DATA(71), + EXT_CLK_MIO_DATA(72), + EXT_CLK_MIO_DATA(73), + EXT_CLK_MIO_DATA(74), + EXT_CLK_MIO_DATA(75), + EXT_CLK_MIO_DATA(76), + EXT_CLK_MIO_DATA(77), +}; + +/* Array of clock which are invalid for this variant */ +static uint32_t pm_clk_invalid_list[] = {CLK_USB0, CLK_USB1, CLK_CSU_SPB, + CLK_ACPU_FULL, + CLK_ACPU_HALF, + CLK_APLL_TO_LPD, + CLK_DBG_FPD, + CLK_DBG_LPD, + CLK_DBG_TRACE, + CLK_DBG_TSTMP, + CLK_DDR_REF, + CLK_TOPSW_MAIN, + CLK_GTGREF0_REF, + CLK_LPD_SWITCH, + CLK_CPU_R5, + CLK_CPU_R5_CORE, + CLK_CSU_SPB, + CLK_CSU_PLL, + CLK_PCAP, + CLK_IOU_SWITCH, + CLK_DLL_REF, + CLK_TIMESTAMP_REF, +}; + +/** + * pm_clock_valid - Check if clock is valid or not. + * @clock_id: Id of the clock to be queried. + * + * This function is used to check if given clock is valid + * or not for the chip variant. + * + * List of invalid clocks are maintained in array list for + * different variants. + * + * Return: Returns 1 if clock is valid else 0. + * + */ +static bool pm_clock_valid(uint32_t clock_id) +{ + unsigned int i; + + for (i = 0U; i < ARRAY_SIZE(pm_clk_invalid_list); i++) + if (pm_clk_invalid_list[i] == clock_id) + return 0; + + return 1; +} + +/** + * pm_clock_type - Get clock's type. + * @clock_id: Id of the clock to be queried. + * + * This function is used to check type of clock (OUTPUT/EXTERNAL). + * + * Return: Returns type of clock (OUTPUT/EXTERNAL). + * + */ +static uint32_t pm_clock_type(uint32_t clock_id) +{ + return (clock_id < CLK_MAX_OUTPUT_CLK) ? + CLK_TYPE_OUTPUT : CLK_TYPE_EXTERNAL; +} + +/** + * pm_api_clock_get_num_clocks() - PM call to request number of clocks. + * @nclocks: Number of clocks. + * + * This function is used by master to get number of clocks. + * + * Return: Returns success. + * + */ +enum pm_ret_status pm_api_clock_get_num_clocks(uint32_t *nclocks) +{ + *nclocks = CLK_MAX; + + return PM_RET_SUCCESS; +} + +/** + * pm_api_clock_get_name() - PM call to request a clock's name. + * @clock_id: Clock ID. + * @name: Name of clock (max 16 bytes). + * + * This function is used by master to get nmae of clock specified + * by given clock ID. + * + */ +void pm_api_clock_get_name(uint32_t clock_id, char *name) +{ + if (clock_id == CLK_MAX) { + memcpy(name, END_OF_CLK, sizeof(END_OF_CLK) > CLK_NAME_LEN ? + CLK_NAME_LEN : sizeof(END_OF_CLK)); + } else if ((clock_id > CLK_MAX) || (!pm_clock_valid(clock_id))) { + memset(name, 0, CLK_NAME_LEN); + } else if (clock_id < CLK_MAX_OUTPUT_CLK) { + memcpy(name, clocks[clock_id].name, CLK_NAME_LEN); + } else { + memcpy(name, ext_clocks[clock_id - CLK_MAX_OUTPUT_CLK].name, + CLK_NAME_LEN); + } +} + +/** + * pm_api_clock_get_topology() - PM call to request a clock's topology. + * @clock_id: Clock ID. + * @index: Topology index for next toplogy node. + * @topology: Buffer to store nodes in topology and flags. + * + * This function is used by master to get topology information for the + * clock specified by given clock ID. Each response would return 3 + * topology nodes. To get next nodes, caller needs to call this API with + * index of next node. Index starts from 0. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_clock_get_topology(uint32_t clock_id, + uint32_t index, + uint32_t *topology) +{ + struct pm_clock_node *clock_nodes; + uint8_t num_nodes; + uint32_t i; + uint16_t typeflags; + + if (!pm_clock_valid(clock_id)) { + return PM_RET_ERROR_ARGS; + } + + if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) { + return PM_RET_ERROR_NOTSUPPORTED; + } + + memset(topology, 0, CLK_TOPOLOGY_PAYLOAD_LEN); + clock_nodes = *clocks[clock_id].nodes; + num_nodes = clocks[clock_id].num_nodes; + + /* Skip parent till index */ + if (index >= num_nodes) { + return PM_RET_SUCCESS; + } + + for (i = 0; i < 3U; i++) { + if ((index + i) == num_nodes) { + break; + } + + topology[i] = clock_nodes[index + i].type; + topology[i] |= clock_nodes[index + i].clkflags << + CLK_CLKFLAGS_SHIFT; + typeflags = clock_nodes[index + i].typeflags; + topology[i] |= (typeflags & CLK_TYPEFLAGS_BITS_MASK) << + CLK_TYPEFLAGS_SHIFT; + topology[i] |= (typeflags & CLK_TYPEFLAGS2_BITS_MASK) >> + (CLK_TYPEFLAGS_BITS - CLK_TYPEFLAGS2_SHIFT); + } + + return PM_RET_SUCCESS; +} + +/** + * pm_api_clock_get_fixedfactor_params() - PM call to request a clock's fixed + * factor parameters for fixed clock. + * @clock_id: Clock ID. + * @mul: Multiplication value. + * @div: Divisor value. + * + * This function is used by master to get fixed factor parameers for the + * fixed clock. This API is application only for the fixed clock. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_clock_get_fixedfactor_params(uint32_t clock_id, + uint32_t *mul, + uint32_t *div) +{ + struct pm_clock_node *clock_nodes; + uint8_t num_nodes; + uint32_t type, i; + + if (!pm_clock_valid(clock_id)) { + return PM_RET_ERROR_ARGS; + } + + if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) { + return PM_RET_ERROR_NOTSUPPORTED; + } + + clock_nodes = *clocks[clock_id].nodes; + num_nodes = clocks[clock_id].num_nodes; + + for (i = 0; i < num_nodes; i++) { + type = clock_nodes[i].type; + if (type == TYPE_FIXEDFACTOR) { + *mul = clock_nodes[i].mult; + *div = clock_nodes[i].div; + break; + } + } + + /* Clock is not fixed clock */ + if (i == num_nodes) { + return PM_RET_ERROR_ARGS; + } + + return PM_RET_SUCCESS; +} + +/** + * pm_api_clock_get_parents() - PM call to request a clock's first 3 parents. + * @clock_id: Clock ID. + * @index: Index of next parent. + * @parents: Parents of the given clock. + * + * This function is used by master to get clock's parents information. + * This API will return 3 parents with a single response. To get other + * parents, master should call same API in loop with new parent index + * till error is returned. + * + * E.g First call should have index 0 which will return parents 0, 1 and + * 2. Next call, index should be 3 which will return parent 3,4 and 5 and + * so on. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_clock_get_parents(uint32_t clock_id, + uint32_t index, + uint32_t *parents) +{ + uint32_t i; + int32_t *clk_parents; + + if (!pm_clock_valid(clock_id)) { + return PM_RET_ERROR_ARGS; + } + + if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) { + return PM_RET_ERROR_NOTSUPPORTED; + } + + clk_parents = *clocks[clock_id].parents; + if (clk_parents == NULL) { + return PM_RET_ERROR_ARGS; + } + + memset(parents, 0, CLK_PARENTS_PAYLOAD_LEN); + + /* Skip parent till index */ + for (i = 0; i < index; i++) { + if (clk_parents[i] == CLK_NA_PARENT) { + return PM_RET_SUCCESS; + } + } + + for (i = 0; i < 3U; i++) { + parents[i] = clk_parents[index + i]; + if (clk_parents[index + i] == CLK_NA_PARENT) { + break; + } + } + + return PM_RET_SUCCESS; +} + +/** + * pm_api_clock_get_attributes() - PM call to request a clock's attributes. + * @clock_id: Clock ID. + * @attr: Clock attributes. + * + * This function is used by master to get clock's attributes + * (e.g. valid, clock type, etc). + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_clock_get_attributes(uint32_t clock_id, + uint32_t *attr) +{ + if (clock_id >= CLK_MAX) { + return PM_RET_ERROR_ARGS; + } + + /* Clock valid bit */ + *attr = pm_clock_valid(clock_id); + + /* Clock type (Output/External) */ + *attr |= (pm_clock_type(clock_id) << CLK_TYPE_SHIFT); + + return PM_RET_SUCCESS; +} + +/** + * pm_api_clock_get_max_divisor - PM call to get max divisor. + * @clock_id: Clock ID. + * @div_type: Divisor Type (TYPE_DIV1 or TYPE_DIV2). + * @max_div: Maximum supported divisor. + * + * This function is used by master to get maximum supported value. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id, + uint8_t div_type, + uint32_t *max_div) +{ + uint32_t i; + struct pm_clock_node *nodes; + + if (clock_id >= CLK_MAX_OUTPUT_CLK) { + return PM_RET_ERROR_ARGS; + } + + nodes = *clocks[clock_id].nodes; + for (i = 0; i < clocks[clock_id].num_nodes; i++) { + if (nodes[i].type == div_type) { + if (CLK_DIVIDER_POWER_OF_TWO & + nodes[i].typeflags) { + *max_div = (1U << (BIT(nodes[i].width) - 1U)); + } else { + *max_div = BIT(nodes[i].width) - 1U; + } + return PM_RET_SUCCESS; + } + } + + return PM_RET_ERROR_ARGS; +} + +/** + * struct pm_pll - PLL related data required to map IOCTL-based PLL control. + * implemented by linux to system-level EEMI APIs. + * @nid: PLL node ID. + * @cid: PLL clock ID. + * @pre_src: Pre-source PLL clock ID. + * @post_src: Post-source PLL clock ID. + * @div2: DIV2 PLL clock ID. + * @bypass: PLL output clock ID that maps to bypass select output. + * @mode: PLL mode currently set via IOCTL (PLL_FRAC_MODE/PLL_INT_MODE). + * + */ +struct pm_pll { + const enum pm_node_id nid; + const enum clock_id cid; + const enum clock_id pre_src; + const enum clock_id post_src; + const enum clock_id div2; + const enum clock_id bypass; + uint8_t mode; +}; + +static struct pm_pll pm_plls[] = { + { + .nid = NODE_IOPLL, + .cid = CLK_IOPLL_INT, + .pre_src = CLK_IOPLL_PRE_SRC, + .post_src = CLK_IOPLL_POST_SRC, + .div2 = CLK_IOPLL_INT_MUX, + .bypass = CLK_IOPLL, + }, { + .nid = NODE_RPLL, + .cid = CLK_RPLL_INT, + .pre_src = CLK_RPLL_PRE_SRC, + .post_src = CLK_RPLL_POST_SRC, + .div2 = CLK_RPLL_INT_MUX, + .bypass = CLK_RPLL, + }, { + .nid = NODE_APLL, + .cid = CLK_APLL_INT, + .pre_src = CLK_APLL_PRE_SRC, + .post_src = CLK_APLL_POST_SRC, + .div2 = CLK_APLL_INT_MUX, + .bypass = CLK_APLL, + }, { + .nid = NODE_VPLL, + .cid = CLK_VPLL_INT, + .pre_src = CLK_VPLL_PRE_SRC, + .post_src = CLK_VPLL_POST_SRC, + .div2 = CLK_VPLL_INT_MUX, + .bypass = CLK_VPLL, + }, { + .nid = NODE_DPLL, + .cid = CLK_DPLL_INT, + .pre_src = CLK_DPLL_PRE_SRC, + .post_src = CLK_DPLL_POST_SRC, + .div2 = CLK_DPLL_INT_MUX, + .bypass = CLK_DPLL, + }, +}; + +/** + * pm_clock_get_pll() - Get PLL structure by PLL clock ID. + * @clock_id: Clock ID of the target PLL. + * + * Return: Pointer to PLL structure if found, NULL otherwise. + * + */ +struct pm_pll *pm_clock_get_pll(enum clock_id clock_id) +{ + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(pm_plls); i++) { + if (pm_plls[i].cid == clock_id) { + return &pm_plls[i]; + } + } + + return NULL; +} + +/** + * pm_clock_get_pll_node_id() - Get PLL node ID by PLL clock ID. + * @clock_id: Clock ID of the target PLL. + * @node_id: Location to store node ID of the target PLL. + * + * Return: PM_RET_SUCCESS if node ID is found, PM_RET_ERROR_ARGS otherwise. + * + */ +enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, + enum pm_node_id *node_id) +{ + struct pm_pll *pll = pm_clock_get_pll(clock_id); + + if (pll) { + *node_id = pll->nid; + return PM_RET_SUCCESS; + } + + return PM_RET_ERROR_ARGS; +} + +/** + * pm_clock_get_pll_by_related_clk() - Get PLL structure by PLL-related clock + * ID. + * @clock_id: Clock ID. + * + * Return: Pointer to PLL structure if found, NULL otherwise. + * + */ +struct pm_pll *pm_clock_get_pll_by_related_clk(enum clock_id clock_id) +{ + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(pm_plls); i++) { + if (pm_plls[i].pre_src == clock_id || + pm_plls[i].post_src == clock_id || + pm_plls[i].div2 == clock_id || + pm_plls[i].bypass == clock_id) { + return &pm_plls[i]; + } + } + + return NULL; +} + +/** + * pm_clock_pll_enable() - "Enable" the PLL clock (lock the PLL). + * @pll: PLL to be locked. + * + * This function is used to map IOCTL/linux-based PLL handling to system-level + * EEMI APIs. + * + * Return: Error if the argument is not valid or status as returned by PMU. + * + */ +enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll) +{ + if (pll == NULL) { + return PM_RET_ERROR_ARGS; + } + + /* Set the PLL mode according to the buffered mode value */ + if (pll->mode == PLL_FRAC_MODE) { + return pm_pll_set_mode(pll->nid, PM_PLL_MODE_FRACTIONAL); + } + + return pm_pll_set_mode(pll->nid, PM_PLL_MODE_INTEGER); +} + +/** + * pm_clock_pll_disable - "Disable" the PLL clock (bypass/reset the PLL). + * @pll: PLL to be bypassed/reset. + * + * This function is used to map IOCTL/linux-based PLL handling to system-level + * EEMI APIs. + * + * Return: Error if the argument is not valid or status as returned by PMU. + * + */ +enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll) +{ + if (pll == NULL) { + return PM_RET_ERROR_ARGS; + } + + return pm_pll_set_mode(pll->nid, PM_PLL_MODE_RESET); +} + +/** + * pm_clock_pll_get_state - Get state of the PLL. + * @pll: Pointer to the target PLL structure. + * @state: Location to store the state: 1/0 ("Enabled"/"Disabled"). + * + * "Enable" actually means that the PLL is locked and its bypass is deasserted, + * "Disable" means that it is bypassed. + * + * Return: PM_RET_ERROR_ARGS error if the argument is not valid, success if + * returned state value is valid or an error if returned by PMU. + */ +enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, + uint32_t *state) +{ + enum pm_ret_status status; + enum pm_pll_mode mode; + + if ((pll == NULL) || !state) { + return PM_RET_ERROR_ARGS; + } + + status = pm_pll_get_mode(pll->nid, &mode); + if (status != PM_RET_SUCCESS) { + return status; + } + + if (mode == PM_PLL_MODE_RESET) { + *state = 0; + } else { + *state = 1; + } + + return PM_RET_SUCCESS; +} + +/** + * pm_clock_pll_set_parent - Set the clock parent for PLL-related clock id. + * @pll: Target PLL structure. + * @clock_id: Id of the clock. + * @parent_index: parent index (=mux select value). + * + * The whole clock-tree implementation relies on the fact that parent indexes + * match to the multiplexer select values. This function has to rely on that + * assumption as well => parent_index is actually the mux select value. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_clock_pll_set_parent(struct pm_pll *pll, + enum clock_id clock_id, + uint32_t parent_index) +{ + if (pll == NULL) { + return PM_RET_ERROR_ARGS; + } + if (pll->pre_src == clock_id) { + return pm_pll_set_parameter(pll->nid, PM_PLL_PARAM_PRE_SRC, + parent_index); + } + if (pll->post_src == clock_id) { + return pm_pll_set_parameter(pll->nid, PM_PLL_PARAM_POST_SRC, + parent_index); + } + if (pll->div2 == clock_id) { + return pm_pll_set_parameter(pll->nid, PM_PLL_PARAM_DIV2, + parent_index); + } + + return PM_RET_ERROR_ARGS; +} + +/** + * pm_clock_pll_get_parent - Get mux select value of PLL-related clock parent. + * @pll: Target PLL structure. + * @clock_id: Id of the clock. + * @parent_index: parent index (=mux select value). + * + * This function is used by master to get parent index for PLL-related clock. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_clock_pll_get_parent(struct pm_pll *pll, + enum clock_id clock_id, + uint32_t *parent_index) +{ + if (pll == NULL) { + return PM_RET_ERROR_ARGS; + } + if (pll->pre_src == clock_id) { + return pm_pll_get_parameter(pll->nid, PM_PLL_PARAM_PRE_SRC, + parent_index); + } + if (pll->post_src == clock_id) { + return pm_pll_get_parameter(pll->nid, PM_PLL_PARAM_POST_SRC, + parent_index); + } + if (pll->div2 == clock_id) { + return pm_pll_get_parameter(pll->nid, PM_PLL_PARAM_DIV2, + parent_index); + } + if (pll->bypass == clock_id) { + *parent_index = 0; + return PM_RET_SUCCESS; + } + + return PM_RET_ERROR_ARGS; +} + +/** + * pm_clock_set_pll_mode() - Set PLL mode. + * @clock_id: PLL clock id. + * @mode: Mode fractional/integer. + * + * This function buffers/saves the PLL mode that is set. + * + * Return: Success if mode is buffered or error if an argument is invalid. + * + */ +enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, + uint32_t mode) +{ + struct pm_pll *pll = pm_clock_get_pll(clock_id); + + if ((pll == NULL) || (mode != PLL_FRAC_MODE && mode != PLL_INT_MODE)) { + return PM_RET_ERROR_ARGS; + } + pll->mode = mode; + + return PM_RET_SUCCESS; +} + +/** + * pm_clock_get_pll_mode() - Get PLL mode. + * @clock_id: PLL clock id. + * @mode: Location to store the mode (fractional/integer). + * + * This function returns buffered PLL mode. + * + * Return: Success if mode is stored or error if an argument is invalid. + * + */ +enum pm_ret_status pm_clock_get_pll_mode(enum clock_id clock_id, + uint32_t *mode) +{ + struct pm_pll *pll = pm_clock_get_pll(clock_id); + + if ((pll == NULL) || !mode) { + return PM_RET_ERROR_ARGS; + } + *mode = pll->mode; + + return PM_RET_SUCCESS; +} + +/** + * pm_clock_id_is_valid() - Check if given clock ID is valid. + * @clock_id: ID of the clock to be checked. + * + * Return: Returns success if clock_id is valid, otherwise an error. + * + */ +enum pm_ret_status pm_clock_id_is_valid(uint32_t clock_id) +{ + if (!pm_clock_valid(clock_id)) { + return PM_RET_ERROR_ARGS; + } + + if (pm_clock_type(clock_id) != CLK_TYPE_OUTPUT) { + return PM_RET_ERROR_NOTSUPPORTED; + } + + return PM_RET_SUCCESS; +} + +/** + * pm_clock_has_div() - Check if the clock has divider with given ID. + * @clock_id: Clock ID. + * @div_id: Divider ID. + * + * Return: True(1)=clock has the divider, false(0)=otherwise. + * + */ +uint8_t pm_clock_has_div(uint32_t clock_id, enum pm_clock_div_id div_id) +{ + uint32_t i; + struct pm_clock_node *nodes; + + if (clock_id >= CLK_MAX_OUTPUT_CLK) { + return 0; + } + + nodes = *clocks[clock_id].nodes; + for (i = 0; i < clocks[clock_id].num_nodes; i++) { + if (nodes[i].type == TYPE_DIV1) { + if (div_id == PM_CLOCK_DIV0_ID) + return 1; + } else if (nodes[i].type == TYPE_DIV2) { + if (div_id == PM_CLOCK_DIV1_ID) + return 1; + } else { + /* To fix the misra 15.7 warning */ + } + } + + return 0; +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h new file mode 100644 index 0000000..3498f91 --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * ZynqMP system level PM-API functions for clock control. + */ + +#ifndef PM_API_CLOCK_H +#define PM_API_CLOCK_H + +#include <lib/utils_def.h> + +#include "pm_common.h" + +#define CLK_NAME_LEN (15U) +#define MAX_PARENTS (100U) +#define CLK_NA_PARENT -1 +#define CLK_DUMMY_PARENT -2 + +/* Flags for parent id */ +#define PARENT_CLK_SELF (0U) +#define PARENT_CLK_NODE1 (1U) +#define PARENT_CLK_NODE2 (2U) +#define PARENT_CLK_NODE3 (3U) +#define PARENT_CLK_NODE4 (4U) +#define PARENT_CLK_EXTERNAL (5U) +#define PARENT_CLK_MIO0_MIO77 (6U) + +#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */ +#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */ +#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ +#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ +/* unused */ +#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ +#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ +#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ +#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ +#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ +#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */ +#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ +/* parents need enable during gate/ungate, set rate and re-parent */ +#define CLK_OPS_PARENT_ENABLE BIT(12) + +#define CLK_DIVIDER_ONE_BASED BIT(0) +#define CLK_DIVIDER_POWER_OF_TWO BIT(1) +#define CLK_DIVIDER_ALLOW_ZERO BIT(2) +#define CLK_DIVIDER_HIWORD_MASK BIT(3) +#define CLK_DIVIDER_ROUND_CLOSEST BIT(4) +#define CLK_DIVIDER_READ_ONLY BIT(5) +#define CLK_DIVIDER_MAX_AT_ZERO BIT(6) +#define CLK_FRAC BIT(8) + +#define END_OF_CLK "END_OF_CLK" + +//CLock Ids +enum clock_id { + CLK_IOPLL = (0U), + CLK_RPLL = (1U), + CLK_APLL = (2U), + CLK_DPLL = (3U), + CLK_VPLL = (4U), + CLK_IOPLL_TO_FPD = (5U), + CLK_RPLL_TO_FPD = (6U), + CLK_APLL_TO_LPD = (7U), + CLK_DPLL_TO_LPD = (8U), + CLK_VPLL_TO_LPD = (9U), + CLK_ACPU = (10U), + CLK_ACPU_HALF = (11U), + CLK_DBG_FPD = (12U), + CLK_DBG_LPD = (13U), + CLK_DBG_TRACE = (14U), + CLK_DBG_TSTMP = (15U), + CLK_DP_VIDEO_REF = (16U), + CLK_DP_AUDIO_REF = (17U), + CLK_DP_STC_REF = (18U), + CLK_GDMA_REF = (19U), + CLK_DPDMA_REF = (20U), + CLK_DDR_REF = (21U), + CLK_SATA_REF = (22U), + CLK_PCIE_REF = (23U), + CLK_GPU_REF = (24U), + CLK_GPU_PP0_REF = (25U), + CLK_GPU_PP1_REF = (26U), + CLK_TOPSW_MAIN = (27U), + CLK_TOPSW_LSBUS = (28U), + CLK_GTGREF0_REF = (29U), + CLK_LPD_SWITCH = (30U), + CLK_LPD_LSBUS = (31U), + CLK_USB0_BUS_REF = (32U), + CLK_USB1_BUS_REF = (33U), + CLK_USB3_DUAL_REF = (34U), + CLK_USB0 = (35U), + CLK_USB1 = (36U), + CLK_CPU_R5 = (37U), + CLK_CPU_R5_CORE = (38U), + CLK_CSU_SPB = (39U), + CLK_CSU_PLL = (40U), + CLK_PCAP = (41U), + CLK_IOU_SWITCH = (42U), + CLK_GEM_TSU_REF = (43U), + CLK_GEM_TSU = (44U), + CLK_GEM0_TX = (45U), + CLK_GEM1_TX = (46U), + CLK_GEM2_TX = (47U), + CLK_GEM3_TX = (48U), + CLK_GEM0_RX = (49U), + CLK_GEM1_RX = (50U), + CLK_GEM2_RX = (51U), + CLK_GEM3_RX = (52U), + CLK_QSPI_REF = (53U), + CLK_SDIO0_REF = (54U), + CLK_SDIO1_REF = (55U), + CLK_UART0_REF = (56U), + CLK_UART1_REF = (57U), + CLK_SPI0_REF = (58U), + CLK_SPI1_REF = (59U), + CLK_NAND_REF = (60U), + CLK_I2C0_REF = (61U), + CLK_I2C1_REF = (62U), + CLK_CAN0_REF = (63U), + CLK_CAN1_REF = (64U), + CLK_CAN0 = (65U), + CLK_CAN1 = (66U), + CLK_DLL_REF = (67U), + CLK_ADMA_REF = (68U), + CLK_TIMESTAMP_REF = (69U), + CLK_AMS_REF = (70U), + CLK_PL0_REF = (71U), + CLK_PL1_REF = (72U), + CLK_PL2_REF = (73U), + CLK_PL3_REF = (74U), + CLK_FPD_WDT = (75U), + CLK_IOPLL_INT = (76U), + CLK_IOPLL_PRE_SRC = (77U), + CLK_IOPLL_HALF = (78U), + CLK_IOPLL_INT_MUX = (79U), + CLK_IOPLL_POST_SRC = (80U), + CLK_RPLL_INT = (81U), + CLK_RPLL_PRE_SRC = (82U), + CLK_RPLL_HALF = (83U), + CLK_RPLL_INT_MUX = (84U), + CLK_RPLL_POST_SRC = (85U), + CLK_APLL_INT = (86U), + CLK_APLL_PRE_SRC = (87U), + CLK_APLL_HALF = (88U), + CLK_APLL_INT_MUX = (89U), + CLK_APLL_POST_SRC = (90U), + CLK_DPLL_INT = (91U), + CLK_DPLL_PRE_SRC = (92U), + CLK_DPLL_HALF = (93U), + CLK_DPLL_INT_MUX = (94U), + CLK_DPLL_POST_SRC = (95U), + CLK_VPLL_INT = (96U), + CLK_VPLL_PRE_SRC = (97U), + CLK_VPLL_HALF = (98U), + CLK_VPLL_INT_MUX = (99U), + CLK_VPLL_POST_SRC = (100U), + CLK_CAN0_MIO = (101U), + CLK_CAN1_MIO = (102U), + CLK_ACPU_FULL = (103U), + CLK_GEM0_REF = (104U), + CLK_GEM1_REF = (105U), + CLK_GEM2_REF = (106U), + CLK_GEM3_REF = (107U), + CLK_GEM0_REF_UNGATED = (108U), + CLK_GEM1_REF_UNGATED = (109U), + CLK_GEM2_REF_UNGATED = (110U), + CLK_GEM3_REF_UNGATED = (111U), + CLK_LPD_WDT = (112U), + END_OF_OUTPUT_CLKS = (113U), +}; + +#define CLK_MAX_OUTPUT_CLK END_OF_OUTPUT_CLKS + +//External clock ids +enum { + EXT_CLK_PSS_REF = END_OF_OUTPUT_CLKS, + EXT_CLK_VIDEO = (114U), + EXT_CLK_PSS_ALT_REF = (115U), + EXT_CLK_AUX_REF = (116U), + EXT_CLK_GT_CRX_REF = (117U), + EXT_CLK_SWDT0 = (118U), + EXT_CLK_SWDT1 = (119U), + EXT_CLK_GEM0_TX_EMIO = (120U), + EXT_CLK_GEM1_TX_EMIO = (121U), + EXT_CLK_GEM2_TX_EMIO = (122U), + EXT_CLK_GEM3_TX_EMIO = (123U), + EXT_CLK_GEM0_RX_EMIO = (124U), + EXT_CLK_GEM1_RX_EMIO = (125U), + EXT_CLK_GEM2_RX_EMIO = (126U), + EXT_CLK_GEM3_RX_EMIO = (127U), + EXT_CLK_MIO50_OR_MIO51 = (128U), + EXT_CLK_MIO0 = (129U), + EXT_CLK_MIO1 = (130U), + EXT_CLK_MIO2 = (131U), + EXT_CLK_MIO3 = (132U), + EXT_CLK_MIO4 = (133U), + EXT_CLK_MIO5 = (134U), + EXT_CLK_MIO6 = (135U), + EXT_CLK_MIO7 = (136U), + EXT_CLK_MIO8 = (137U), + EXT_CLK_MIO9 = (138U), + EXT_CLK_MIO10 = (139U), + EXT_CLK_MIO11 = (140U), + EXT_CLK_MIO12 = (141U), + EXT_CLK_MIO13 = (142U), + EXT_CLK_MIO14 = (143U), + EXT_CLK_MIO15 = (144U), + EXT_CLK_MIO16 = (145U), + EXT_CLK_MIO17 = (146U), + EXT_CLK_MIO18 = (147U), + EXT_CLK_MIO19 = (148U), + EXT_CLK_MIO20 = (149U), + EXT_CLK_MIO21 = (150U), + EXT_CLK_MIO22 = (151U), + EXT_CLK_MIO23 = (152U), + EXT_CLK_MIO24 = (153U), + EXT_CLK_MIO25 = (154U), + EXT_CLK_MIO26 = (155U), + EXT_CLK_MIO27 = (156U), + EXT_CLK_MIO28 = (157U), + EXT_CLK_MIO29 = (158U), + EXT_CLK_MIO30 = (159U), + EXT_CLK_MIO31 = (160U), + EXT_CLK_MIO32 = (161U), + EXT_CLK_MIO33 = (162U), + EXT_CLK_MIO34 = (163U), + EXT_CLK_MIO35 = (164U), + EXT_CLK_MIO36 = (165U), + EXT_CLK_MIO37 = (166U), + EXT_CLK_MIO38 = (167U), + EXT_CLK_MIO39 = (168U), + EXT_CLK_MIO40 = (169U), + EXT_CLK_MIO41 = (170U), + EXT_CLK_MIO42 = (171U), + EXT_CLK_MIO43 = (172U), + EXT_CLK_MIO44 = (173U), + EXT_CLK_MIO45 = (174U), + EXT_CLK_MIO46 = (175U), + EXT_CLK_MIO47 = (176U), + EXT_CLK_MIO48 = (177U), + EXT_CLK_MIO49 = (178U), + EXT_CLK_MIO50 = (179U), + EXT_CLK_MIO51 = (180U), + EXT_CLK_MIO52 = (181U), + EXT_CLK_MIO53 = (182U), + EXT_CLK_MIO54 = (183U), + EXT_CLK_MIO55 = (184U), + EXT_CLK_MIO56 = (185U), + EXT_CLK_MIO57 = (186U), + EXT_CLK_MIO58 = (187U), + EXT_CLK_MIO59 = (188U), + EXT_CLK_MIO60 = (189U), + EXT_CLK_MIO61 = (190U), + EXT_CLK_MIO62 = (191U), + EXT_CLK_MIO63 = (192U), + EXT_CLK_MIO64 = (193U), + EXT_CLK_MIO65 = (194U), + EXT_CLK_MIO66 = (195U), + EXT_CLK_MIO67 = (196U), + EXT_CLK_MIO68 = (197U), + EXT_CLK_MIO69 = (198U), + EXT_CLK_MIO70 = (199U), + EXT_CLK_MIO71 = (200U), + EXT_CLK_MIO72 = (201U), + EXT_CLK_MIO73 = (202U), + EXT_CLK_MIO74 = (203U), + EXT_CLK_MIO75 = (204U), + EXT_CLK_MIO76 = (205U), + EXT_CLK_MIO77 = (206U), + END_OF_CLKS = (207U), +}; + +#define CLK_MAX END_OF_CLKS + +//CLock types +#define CLK_TYPE_OUTPUT 0U +#define CLK_TYPE_EXTERNAL 1U + +//Topology types +#define TYPE_INVALID 0U +#define TYPE_MUX 1U +#define TYPE_PLL 2U +#define TYPE_FIXEDFACTOR 3U +#define TYPE_DIV1 4U +#define TYPE_DIV2 5U +#define TYPE_GATE 6U + +struct pm_pll; +struct pm_pll *pm_clock_get_pll(enum clock_id clock_id); +struct pm_pll *pm_clock_get_pll_by_related_clk(enum clock_id clock_id); +uint8_t pm_clock_has_div(uint32_t clock_id, enum pm_clock_div_id div_id); + +void pm_api_clock_get_name(uint32_t clock_id, char *name); +enum pm_ret_status pm_api_clock_get_num_clocks(uint32_t *nclocks); +enum pm_ret_status pm_api_clock_get_topology(uint32_t clock_id, + uint32_t index, + uint32_t *topology); +enum pm_ret_status pm_api_clock_get_fixedfactor_params(uint32_t clock_id, + uint32_t *mul, + uint32_t *div); +enum pm_ret_status pm_api_clock_get_parents(uint32_t clock_id, + uint32_t index, + uint32_t *parents); +enum pm_ret_status pm_api_clock_get_attributes(uint32_t clock_id, + uint32_t *attr); +enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id, + uint8_t div_type, + uint32_t *max_div); + +enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, + enum pm_node_id *node_id); +enum pm_ret_status pm_clock_id_is_valid(uint32_t clock_id); + +enum pm_ret_status pm_clock_pll_enable(struct pm_pll *pll); +enum pm_ret_status pm_clock_pll_disable(struct pm_pll *pll); +enum pm_ret_status pm_clock_pll_get_state(struct pm_pll *pll, + uint32_t *state); +enum pm_ret_status pm_clock_pll_set_parent(struct pm_pll *pll, + enum clock_id clock_id, + uint32_t parent_index); +enum pm_ret_status pm_clock_pll_get_parent(struct pm_pll *pll, + enum clock_id clock_id, + uint32_t *parent_index); +enum pm_ret_status pm_clock_set_pll_mode(enum clock_id clock_id, + uint32_t mode); +enum pm_ret_status pm_clock_get_pll_mode(enum clock_id clock_id, + uint32_t *mode); + +#endif /* PM_API_CLOCK_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c new file mode 100644 index 0000000..dd21499 --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c @@ -0,0 +1,720 @@ +/* + * Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * ZynqMP system level PM-API functions for ioctl. + */ + +#include <arch_helpers.h> +#include <drivers/delay_timer.h> +#include <lib/mmio.h> +#include <plat/common/platform.h> + +#include "pm_api_clock.h" +#include "pm_api_ioctl.h" +#include "pm_client.h" +#include "pm_common.h" +#include "pm_ipi.h" +#include <zynqmp_def.h> +#include "zynqmp_pm_api_sys.h" + +/** + * pm_ioctl_get_rpu_oper_mode () - Get current RPU operation mode. + * @mode: Buffer to store value of oper mode(Split/Lock-step) + * + * This function provides current configured RPU operational mode. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_get_rpu_oper_mode(uint32_t *mode) +{ + uint32_t val; + + val = mmio_read_32(ZYNQMP_RPU_GLBL_CNTL); + val &= ZYNQMP_SLSPLIT_MASK; + if (val == 0U) { + *mode = PM_RPU_MODE_LOCKSTEP; + } else { + *mode = PM_RPU_MODE_SPLIT; + } + + return PM_RET_SUCCESS; +} + +/** + * pm_ioctl_set_rpu_oper_mode () - Configure RPU operation mode. + * @mode: Value to set for oper mode(Split/Lock-step). + * + * This function configures RPU operational mode(Split/Lock-step). + * It also sets TCM combined mode in RPU lock-step and TCM non-combined + * mode for RPU split mode. In case of Lock step mode, RPU1's output is + * clamped. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_set_rpu_oper_mode(uint32_t mode) +{ + uint32_t val; + + if (mmio_read_32(CRL_APB_RST_LPD_TOP) & CRL_APB_RPU_AMBA_RESET) { + return PM_RET_ERROR_ACCESS; + } + + val = mmio_read_32(ZYNQMP_RPU_GLBL_CNTL); + + if (mode == PM_RPU_MODE_SPLIT) { + val |= ZYNQMP_SLSPLIT_MASK; + val &= ~ZYNQMP_TCM_COMB_MASK; + val &= ~ZYNQMP_SLCLAMP_MASK; + } else if (mode == PM_RPU_MODE_LOCKSTEP) { + val &= ~ZYNQMP_SLSPLIT_MASK; + val |= ZYNQMP_TCM_COMB_MASK; + val |= ZYNQMP_SLCLAMP_MASK; + } else { + return PM_RET_ERROR_ARGS; + } + + mmio_write_32(ZYNQMP_RPU_GLBL_CNTL, val); + + return PM_RET_SUCCESS; +} + +/** + * pm_ioctl_config_boot_addr() - Configure RPU boot address. + * @nid: Node ID of RPU. + * @value: Value to set for boot address (TCM/OCM). + * + * This function configures RPU boot address(memory). + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_config_boot_addr(enum pm_node_id nid, + uint32_t value) +{ + uint32_t rpu_cfg_addr, val; + + if (nid == NODE_RPU_0) { + rpu_cfg_addr = ZYNQMP_RPU0_CFG; + } else if (nid == NODE_RPU_1) { + rpu_cfg_addr = ZYNQMP_RPU1_CFG; + } else { + return PM_RET_ERROR_ARGS; + } + + val = mmio_read_32(rpu_cfg_addr); + + if (value == PM_RPU_BOOTMEM_LOVEC) { + val &= ~ZYNQMP_VINITHI_MASK; + } else if (value == PM_RPU_BOOTMEM_HIVEC) { + val |= ZYNQMP_VINITHI_MASK; + } else { + return PM_RET_ERROR_ARGS; + } + + mmio_write_32(rpu_cfg_addr, val); + + return PM_RET_SUCCESS; +} + +/** + * pm_ioctl_config_tcm_comb() - Configure TCM combined mode. + * @value: Value to set (Split/Combined). + * + * This function configures TCM to be in split mode or combined + * mode. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_config_tcm_comb(uint32_t value) +{ + uint32_t val; + + val = mmio_read_32(ZYNQMP_RPU_GLBL_CNTL); + + if (value == PM_RPU_TCM_SPLIT) { + val &= ~ZYNQMP_TCM_COMB_MASK; + } else if (value == PM_RPU_TCM_COMB) { + val |= ZYNQMP_TCM_COMB_MASK; + } else { + return PM_RET_ERROR_ARGS; + } + + mmio_write_32(ZYNQMP_RPU_GLBL_CNTL, val); + + return PM_RET_SUCCESS; +} + +/** + * pm_ioctl_set_tapdelay_bypass() - Enable/Disable tap delay bypass. + * @type: Type of tap delay to enable/disable (e.g. QSPI). + * @value: Enable/Disable. + * + * This function enable/disable tap delay bypass. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_set_tapdelay_bypass(uint32_t type, + uint32_t value) +{ + if ((value != PM_TAPDELAY_BYPASS_ENABLE && + value != PM_TAPDELAY_BYPASS_DISABLE) || type >= PM_TAPDELAY_MAX) { + return PM_RET_ERROR_ARGS; + } + + return pm_mmio_write(IOU_TAPDLY_BYPASS, TAP_DELAY_MASK, value << type); +} + +/** + * pm_ioctl_sd_dll_reset() - Reset DLL logic. + * @nid: Node ID of the device. + * @type: Reset type. + * + * This function resets DLL logic for the SD device. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_sd_dll_reset(enum pm_node_id nid, + uint32_t type) +{ + uint32_t mask, val; + enum pm_ret_status ret; + + if (nid == NODE_SD_0) { + mask = ZYNQMP_SD0_DLL_RST_MASK; + val = ZYNQMP_SD0_DLL_RST; + } else if (nid == NODE_SD_1) { + mask = ZYNQMP_SD1_DLL_RST_MASK; + val = ZYNQMP_SD1_DLL_RST; + } else { + return PM_RET_ERROR_ARGS; + } + + switch (type) { + case PM_DLL_RESET_ASSERT: + case PM_DLL_RESET_PULSE: + ret = pm_mmio_write(ZYNQMP_SD_DLL_CTRL, mask, val); + if (ret != PM_RET_SUCCESS) { + return ret; + } + + if (type == PM_DLL_RESET_ASSERT) { + break; + } + mdelay(1); + /* Fallthrough */ + case PM_DLL_RESET_RELEASE: + ret = pm_mmio_write(ZYNQMP_SD_DLL_CTRL, mask, 0); + break; + default: + ret = PM_RET_ERROR_ARGS; + break; + } + + return ret; +} + +/** + * pm_ioctl_sd_set_tapdelay() - Set tap delay for the SD device. + * @nid: Node ID of the device. + * @type: Type of tap delay to set (input/output). + * @value: Value to set fot the tap delay. + * + * This function sets input/output tap delay for the SD device. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_sd_set_tapdelay(enum pm_node_id nid, + enum tap_delay_type type, + uint32_t value) +{ + uint32_t shift; + enum pm_ret_status ret; + uint32_t val, mask; + + if (nid == NODE_SD_0) { + shift = 0; + mask = ZYNQMP_SD0_DLL_RST_MASK; + } else if (nid == NODE_SD_1) { + shift = ZYNQMP_SD_TAP_OFFSET; + mask = ZYNQMP_SD1_DLL_RST_MASK; + } else { + return PM_RET_ERROR_ARGS; + } + + ret = pm_mmio_read(ZYNQMP_SD_DLL_CTRL, &val); + if (ret != PM_RET_SUCCESS) { + return ret; + } + + if ((val & mask) == 0U) { + ret = pm_ioctl_sd_dll_reset(nid, PM_DLL_RESET_ASSERT); + if (ret != PM_RET_SUCCESS) { + return ret; + } + } + + if (type == PM_TAPDELAY_INPUT) { + ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY, + (ZYNQMP_SD_ITAPCHGWIN_MASK << shift), + (ZYNQMP_SD_ITAPCHGWIN << shift)); + + if (ret != PM_RET_SUCCESS) { + goto reset_release; + } + + if (value == 0U) { + ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY, + (ZYNQMP_SD_ITAPDLYENA_MASK << + shift), 0); + } else { + ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY, + (ZYNQMP_SD_ITAPDLYENA_MASK << + shift), (ZYNQMP_SD_ITAPDLYENA << + shift)); + } + + if (ret != PM_RET_SUCCESS) { + goto reset_release; + } + + ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY, + (ZYNQMP_SD_ITAPDLYSEL_MASK << shift), + (value << shift)); + + if (ret != PM_RET_SUCCESS) { + goto reset_release; + } + + ret = pm_mmio_write(ZYNQMP_SD_ITAP_DLY, + (ZYNQMP_SD_ITAPCHGWIN_MASK << shift), 0); + } else if (type == PM_TAPDELAY_OUTPUT) { + ret = pm_mmio_write(ZYNQMP_SD_OTAP_DLY, + (ZYNQMP_SD_OTAPDLYENA_MASK << shift), 0); + + if (ret != PM_RET_SUCCESS) { + goto reset_release; + } + + ret = pm_mmio_write(ZYNQMP_SD_OTAP_DLY, + (ZYNQMP_SD_OTAPDLYSEL_MASK << shift), + (value << shift)); + } else { + ret = PM_RET_ERROR_ARGS; + } + +reset_release: + if ((val & mask) == 0) { + (void)pm_ioctl_sd_dll_reset(nid, PM_DLL_RESET_RELEASE); + } + + return ret; +} + +/** + * pm_ioctl_set_pll_frac_mode() - Ioctl function for setting pll mode. + * @pll: PLL clock id. + * @mode: Mode fraction/integar. + * + * This function sets PLL mode. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_set_pll_frac_mode + (uint32_t pll, uint32_t mode) +{ + return pm_clock_set_pll_mode(pll, mode); +} + +/** + * pm_ioctl_get_pll_frac_mode() - Ioctl function for getting pll mode. + * @pll: PLL clock id. + * @mode: Mode fraction/integar. + * + * This function return current PLL mode. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_get_pll_frac_mode + (uint32_t pll, uint32_t *mode) +{ + return pm_clock_get_pll_mode(pll, mode); +} + +/** + * pm_ioctl_set_pll_frac_data() - Ioctl function for setting pll fraction data. + * @pll: PLL clock id. + * @data: fraction data. + * + * This function sets fraction data. + * It is valid for fraction mode only. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_set_pll_frac_data + (uint32_t pll, uint32_t data) +{ + enum pm_node_id pll_nid; + enum pm_ret_status status; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(pll, &pll_nid); + if (status != PM_RET_SUCCESS) { + return status; + } + + return pm_pll_set_parameter(pll_nid, PM_PLL_PARAM_DATA, data); +} + +/** + * pm_ioctl_get_pll_frac_data() - Ioctl function for getting pll fraction data. + * @pll: PLL clock id. + * @data: fraction data. + * + * This function returns fraction data value. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_get_pll_frac_data + (uint32_t pll, uint32_t *data) +{ + enum pm_node_id pll_nid; + enum pm_ret_status status; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(pll, &pll_nid); + if (status != PM_RET_SUCCESS) { + return status; + } + + return pm_pll_get_parameter(pll_nid, PM_PLL_PARAM_DATA, data); +} + +/** + * pm_ioctl_write_ggs() - Ioctl function for writing global general storage + * (ggs). + * @index: GGS register index. + * @value: Register value to be written. + * + * This function writes value to GGS register. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_write_ggs(uint32_t index, + uint32_t value) +{ + if (index >= GGS_NUM_REGS) { + return PM_RET_ERROR_ARGS; + } + + return pm_mmio_write(GGS_BASEADDR + (index << 2), + 0xFFFFFFFFU, value); +} + +/** + * pm_ioctl_read_ggs() - Ioctl function for reading global general storage + * (ggs). + * @index: GGS register index. + * @value: Register value. + * + * This function returns GGS register value. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_read_ggs(uint32_t index, + uint32_t *value) +{ + if (index >= GGS_NUM_REGS) { + return PM_RET_ERROR_ARGS; + } + + return pm_mmio_read(GGS_BASEADDR + (index << 2), value); +} + +/** + * pm_ioctl_write_pggs() - Ioctl function for writing persistent global general + * storage (pggs). + * @index: PGGS register index. + * @value: Register value to be written. + * + * This function writes value to PGGS register. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_write_pggs(uint32_t index, + uint32_t value) +{ + if (index >= PGGS_NUM_REGS) { + return PM_RET_ERROR_ARGS; + } + + return pm_mmio_write(PGGS_BASEADDR + (index << 2), + 0xFFFFFFFFU, value); +} + +/** + * pm_ioctl_afi() - Ioctl function for writing afi values. + * @index: AFI register index. + * @value: Register value to be written. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_afi(uint32_t index, + uint32_t value) +{ + uint32_t mask; + uint32_t regarr[] = {0xFD360000U, + 0xFD360014U, + 0xFD370000U, + 0xFD370014U, + 0xFD380000U, + 0xFD380014U, + 0xFD390000U, + 0xFD390014U, + 0xFD3a0000U, + 0xFD3a0014U, + 0xFD3b0000U, + 0xFD3b0014U, + 0xFF9b0000U, + 0xFF9b0014U, + 0xFD615000U, + 0xFF419000U, + }; + + if (index >= ARRAY_SIZE(regarr)) { + return PM_RET_ERROR_ARGS; + } + + if (index <= AFIFM6_WRCTRL) { + mask = FABRIC_WIDTH; + } else { + mask = 0xf00; + } + + return pm_mmio_write(regarr[index], mask, value); +} + +/** + * pm_ioctl_read_pggs() - Ioctl function for reading persistent global general + * storage (pggs). + * @index: PGGS register index. + * @value: Register value. + * + * This function returns PGGS register value. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_read_pggs(uint32_t index, + uint32_t *value) +{ + if (index >= PGGS_NUM_REGS) { + return PM_RET_ERROR_ARGS; + } + + return pm_mmio_read(PGGS_BASEADDR + (index << 2), value); +} + +/** + * pm_ioctl_ulpi_reset() - Ioctl function for performing ULPI reset. + * + * Return: Returns status, either success or error+reason. + * + * This function peerforms the ULPI reset sequence for resetting + * the ULPI transceiver. + */ +static enum pm_ret_status pm_ioctl_ulpi_reset(void) +{ + enum pm_ret_status ret; + + ret = pm_mmio_write(CRL_APB_BOOT_PIN_CTRL, CRL_APB_BOOT_PIN_MASK, + ZYNQMP_ULPI_RESET_VAL_HIGH); + if (ret != PM_RET_SUCCESS) { + return ret; + } + + /* Drive ULPI assert for atleast 1ms */ + mdelay(1); + + ret = pm_mmio_write(CRL_APB_BOOT_PIN_CTRL, CRL_APB_BOOT_PIN_MASK, + ZYNQMP_ULPI_RESET_VAL_LOW); + if (ret != PM_RET_SUCCESS) { + return ret; + } + + /* Drive ULPI de-assert for atleast 1ms */ + mdelay(1); + + ret = pm_mmio_write(CRL_APB_BOOT_PIN_CTRL, CRL_APB_BOOT_PIN_MASK, + ZYNQMP_ULPI_RESET_VAL_HIGH); + + return ret; +} + +/** + * pm_ioctl_set_boot_health_status() - Ioctl for setting healthy boot status. + * @value: Value to write. + * + * This function sets healthy bit value to indicate boot health status + * to firmware. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_ioctl_set_boot_health_status(uint32_t value) +{ + return pm_mmio_write(PMU_GLOBAL_GEN_STORAGE4, + PM_BOOT_HEALTH_STATUS_MASK, value); +} + +/** + * pm_api_ioctl() - PM IOCTL API for device control and configs. + * @nid: Node ID of the device. + * @ioctl_id: ID of the requested IOCTL. + * @arg1: Argument 1 to requested IOCTL call. + * @arg2: Argument 2 to requested IOCTL call. + * @value: Returned output value. + * + * This function calls IOCTL to firmware for device control and configuration. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_ioctl(enum pm_node_id nid, + uint32_t ioctl_id, + uint32_t arg1, + uint32_t arg2, + uint32_t *value) +{ + enum pm_ret_status ret; + uint32_t payload[PAYLOAD_ARG_CNT]; + + switch (ioctl_id) { + case IOCTL_GET_RPU_OPER_MODE: + ret = pm_ioctl_get_rpu_oper_mode(value); + break; + case IOCTL_SET_RPU_OPER_MODE: + ret = pm_ioctl_set_rpu_oper_mode(arg1); + break; + case IOCTL_RPU_BOOT_ADDR_CONFIG: + ret = pm_ioctl_config_boot_addr(nid, arg1); + break; + case IOCTL_TCM_COMB_CONFIG: + ret = pm_ioctl_config_tcm_comb(arg1); + break; + case IOCTL_SET_TAPDELAY_BYPASS: + ret = pm_ioctl_set_tapdelay_bypass(arg1, arg2); + break; + case IOCTL_SD_DLL_RESET: + ret = pm_ioctl_sd_dll_reset(nid, arg1); + break; + case IOCTL_SET_SD_TAPDELAY: + ret = pm_ioctl_sd_set_tapdelay(nid, arg1, arg2); + break; + case IOCTL_SET_PLL_FRAC_MODE: + ret = pm_ioctl_set_pll_frac_mode(arg1, arg2); + break; + case IOCTL_GET_PLL_FRAC_MODE: + ret = pm_ioctl_get_pll_frac_mode(arg1, value); + break; + case IOCTL_SET_PLL_FRAC_DATA: + ret = pm_ioctl_set_pll_frac_data(arg1, arg2); + break; + case IOCTL_GET_PLL_FRAC_DATA: + ret = pm_ioctl_get_pll_frac_data(arg1, value); + break; + case IOCTL_WRITE_GGS: + ret = pm_ioctl_write_ggs(arg1, arg2); + break; + case IOCTL_READ_GGS: + ret = pm_ioctl_read_ggs(arg1, value); + break; + case IOCTL_WRITE_PGGS: + ret = pm_ioctl_write_pggs(arg1, arg2); + break; + case IOCTL_READ_PGGS: + ret = pm_ioctl_read_pggs(arg1, value); + break; + case IOCTL_ULPI_RESET: + ret = pm_ioctl_ulpi_reset(); + break; + case IOCTL_SET_BOOT_HEALTH_STATUS: + ret = pm_ioctl_set_boot_health_status(arg1); + break; + case IOCTL_AFI: + ret = pm_ioctl_afi(arg1, arg2); + break; + default: + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_IOCTL, nid, ioctl_id, arg1, arg2); + + ret = pm_ipi_send_sync(primary_proc, payload, value, 1); + break; + } + + return ret; +} + +/** + * tfa_ioctl_bitmask() - API to get supported IOCTL ID mask. + * @bit_mask: Returned bit mask of supported IOCTL IDs. + * + * Return: 0 success, negative value for errors. + * + */ +enum pm_ret_status tfa_ioctl_bitmask(uint32_t *bit_mask) +{ + uint8_t supported_ids[] = { + IOCTL_GET_RPU_OPER_MODE, + IOCTL_SET_RPU_OPER_MODE, + IOCTL_RPU_BOOT_ADDR_CONFIG, + IOCTL_TCM_COMB_CONFIG, + IOCTL_SET_TAPDELAY_BYPASS, + IOCTL_SD_DLL_RESET, + IOCTL_SET_SD_TAPDELAY, + IOCTL_SET_PLL_FRAC_MODE, + IOCTL_GET_PLL_FRAC_MODE, + IOCTL_SET_PLL_FRAC_DATA, + IOCTL_GET_PLL_FRAC_DATA, + IOCTL_WRITE_GGS, + IOCTL_READ_GGS, + IOCTL_WRITE_PGGS, + IOCTL_READ_PGGS, + IOCTL_ULPI_RESET, + IOCTL_SET_BOOT_HEALTH_STATUS, + IOCTL_AFI, + }; + uint8_t i, ioctl_id; + int32_t ret; + + for (i = 0U; i < ARRAY_SIZE(supported_ids); i++) { + ioctl_id = supported_ids[i]; + if (ioctl_id >= 64U) { + return PM_RET_ERROR_NOTSUPPORTED; + } + ret = check_api_dependency(ioctl_id); + if (ret == PM_RET_SUCCESS) { + bit_mask[ioctl_id / 32U] |= BIT(ioctl_id % 32U); + } + } + + return PM_RET_SUCCESS; +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h new file mode 100644 index 0000000..bf94b7c --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/pm_api_ioctl.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * ZynqMP system level PM-API functions for pin control. + */ + +#ifndef PM_API_IOCTL_H +#define PM_API_IOCTL_H + +#include "pm_common.h" + +//RPU operation mode +#define PM_RPU_MODE_LOCKSTEP 0U +#define PM_RPU_MODE_SPLIT 1U + +//RPU boot mem +#define PM_RPU_BOOTMEM_LOVEC 0U +#define PM_RPU_BOOTMEM_HIVEC 1U + +//RPU tcm mpde +#define PM_RPU_TCM_SPLIT 0U +#define PM_RPU_TCM_COMB 1U + +//tap delay signal type +#define PM_TAPDELAY_NAND_DQS_IN 0U +#define PM_TAPDELAY_NAND_DQS_OUT 1U +#define PM_TAPDELAY_QSPI 2U +#define PM_TAPDELAY_MAX 3U + +//tap delay bypass +#define PM_TAPDELAY_BYPASS_DISABLE 0U +#define PM_TAPDELAY_BYPASS_ENABLE 1U + +enum tap_delay_type { + PM_TAPDELAY_INPUT, + PM_TAPDELAY_OUTPUT, +}; + +//dll reset type +#define PM_DLL_RESET_ASSERT 0U +#define PM_DLL_RESET_RELEASE 1U +#define PM_DLL_RESET_PULSE 2U + +enum pm_ret_status pm_api_ioctl(enum pm_node_id nid, + uint32_t ioctl_id, + uint32_t arg1, + uint32_t arg2, + uint32_t *value); +enum pm_ret_status tfa_ioctl_bitmask(uint32_t *bit_mask); +#endif /* PM_API_IOCTL_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c b/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c new file mode 100644 index 0000000..2d8c23b --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c @@ -0,0 +1,2119 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * ZynqMP system level PM-API functions for pin control. + */ + +#include <string.h> + +#include <arch_helpers.h> +#include <plat/common/platform.h> + +#include "pm_api_pinctrl.h" +#include "pm_client.h" +#include "pm_common.h" +#include "pm_ipi.h" +#include "zynqmp_pm_api_sys.h" + +struct pinctrl_function { + char name[FUNCTION_NAME_LEN]; + uint16_t group_base; + uint8_t group_size; + uint8_t regval; +}; + +/* Max groups for one pin */ +#define MAX_PIN_GROUPS (13U) + +struct zynqmp_pin_group { + uint16_t (*groups)[]; +}; + +static struct pinctrl_function pinctrl_functions[MAX_FUNCTION] = { + [PINCTRL_FUNC_CAN0] = { + .name = "can0", + .regval = 0x20, + .group_base = PINCTRL_GRP_CAN0_0, + .group_size = PINCTRL_GRP_CAN0_18 - PINCTRL_GRP_CAN0_0 + 1U, + }, + [PINCTRL_FUNC_CAN1] = { + .name = "can1", + .regval = 0x20, + .group_base = PINCTRL_GRP_CAN1_0, + .group_size = PINCTRL_GRP_CAN1_19 - PINCTRL_GRP_CAN1_0 + 1U, + }, + [PINCTRL_FUNC_ETHERNET0] = { + .name = "ethernet0", + .regval = 0x02, + .group_base = PINCTRL_GRP_ETHERNET0_0, + .group_size = PINCTRL_GRP_ETHERNET0_0 - PINCTRL_GRP_ETHERNET0_0 + 1U, + }, + [PINCTRL_FUNC_ETHERNET1] = { + .name = "ethernet1", + .regval = 0x02, + .group_base = PINCTRL_GRP_ETHERNET1_0, + .group_size = PINCTRL_GRP_ETHERNET1_0 - PINCTRL_GRP_ETHERNET1_0 + 1U, + }, + [PINCTRL_FUNC_ETHERNET2] = { + .name = "ethernet2", + .regval = 0x02, + .group_base = PINCTRL_GRP_ETHERNET2_0, + .group_size = PINCTRL_GRP_ETHERNET2_0 - PINCTRL_GRP_ETHERNET2_0 + 1U, + }, + [PINCTRL_FUNC_ETHERNET3] = { + .name = "ethernet3", + .regval = 0x02, + .group_base = PINCTRL_GRP_ETHERNET3_0, + .group_size = PINCTRL_GRP_ETHERNET3_0 - PINCTRL_GRP_ETHERNET3_0 + 1U, + }, + [PINCTRL_FUNC_GEMTSU0] = { + .name = "gemtsu0", + .regval = 0x02, + .group_base = PINCTRL_GRP_GEMTSU0_0, + .group_size = PINCTRL_GRP_GEMTSU0_2 - PINCTRL_GRP_GEMTSU0_0 + 1U, + }, + [PINCTRL_FUNC_GPIO0] = { + .name = "gpio0", + .regval = 0x00, + .group_base = PINCTRL_GRP_GPIO0_0, + .group_size = PINCTRL_GRP_GPIO0_77 - PINCTRL_GRP_GPIO0_0 + 1U, + }, + [PINCTRL_FUNC_I2C0] = { + .name = "i2c0", + .regval = 0x40, + .group_base = PINCTRL_GRP_I2C0_0, + .group_size = PINCTRL_GRP_I2C0_18 - PINCTRL_GRP_I2C0_0 + 1U, + }, + [PINCTRL_FUNC_I2C1] = { + .name = "i2c1", + .regval = 0x40, + .group_base = PINCTRL_GRP_I2C1_0, + .group_size = PINCTRL_GRP_I2C1_19 - PINCTRL_GRP_I2C1_0 + 1U, + }, + [PINCTRL_FUNC_MDIO0] = { + .name = "mdio0", + .regval = 0x60, + .group_base = PINCTRL_GRP_MDIO0_0, + .group_size = PINCTRL_GRP_MDIO0_0 - PINCTRL_GRP_MDIO0_0 + 1U, + }, + [PINCTRL_FUNC_MDIO1] = { + .name = "mdio1", + .regval = 0x80, + .group_base = PINCTRL_GRP_MDIO1_0, + .group_size = PINCTRL_GRP_MDIO1_1 - PINCTRL_GRP_MDIO1_0 + 1U, + }, + [PINCTRL_FUNC_MDIO2] = { + .name = "mdio2", + .regval = 0xa0, + .group_base = PINCTRL_GRP_MDIO2_0, + .group_size = PINCTRL_GRP_MDIO2_0 - PINCTRL_GRP_MDIO2_0 + 1U, + }, + [PINCTRL_FUNC_MDIO3] = { + .name = "mdio3", + .regval = 0xc0, + .group_base = PINCTRL_GRP_MDIO3_0, + .group_size = PINCTRL_GRP_MDIO3_0 - PINCTRL_GRP_MDIO3_0 + 1U, + }, + [PINCTRL_FUNC_QSPI0] = { + .name = "qspi0", + .regval = 0x02, + .group_base = PINCTRL_GRP_QSPI0_0, + .group_size = PINCTRL_GRP_QSPI0_0 - PINCTRL_GRP_QSPI0_0 + 1U, + }, + [PINCTRL_FUNC_QSPI_FBCLK] = { + .name = "qspi_fbclk", + .regval = 0x02, + .group_base = PINCTRL_GRP_QSPI_FBCLK, + .group_size = PINCTRL_GRP_QSPI_FBCLK - PINCTRL_GRP_QSPI_FBCLK + 1U, + }, + [PINCTRL_FUNC_QSPI_SS] = { + .name = "qspi_ss", + .regval = 0x02, + .group_base = PINCTRL_GRP_QSPI_SS, + .group_size = PINCTRL_GRP_QSPI_SS - PINCTRL_GRP_QSPI_SS + 1U, + }, + [PINCTRL_FUNC_SPI0] = { + .name = "spi0", + .regval = 0x80, + .group_base = PINCTRL_GRP_SPI0_0, + .group_size = PINCTRL_GRP_SPI0_5 - PINCTRL_GRP_SPI0_0 + 1U, + }, + [PINCTRL_FUNC_SPI1] = { + .name = "spi1", + .regval = 0x80, + .group_base = PINCTRL_GRP_SPI1_0, + .group_size = PINCTRL_GRP_SPI1_5 - PINCTRL_GRP_SPI1_0 + 1U, + }, + [PINCTRL_FUNC_SPI0_SS] = { + .name = "spi0_ss", + .regval = 0x80, + .group_base = PINCTRL_GRP_SPI0_0_SS0, + .group_size = PINCTRL_GRP_SPI0_5_SS2 - PINCTRL_GRP_SPI0_0_SS0 + 1U, + }, + [PINCTRL_FUNC_SPI1_SS] = { + .name = "spi1_ss", + .regval = 0x80, + .group_base = PINCTRL_GRP_SPI1_0_SS0, + .group_size = PINCTRL_GRP_SPI1_5_SS2 - PINCTRL_GRP_SPI1_0_SS0 + 1U, + }, + [PINCTRL_FUNC_SDIO0] = { + .name = "sdio0", + .regval = 0x08, + .group_base = PINCTRL_GRP_SDIO0_0, + .group_size = PINCTRL_GRP_SDIO0_1BIT_2_7 - PINCTRL_GRP_SDIO0_0 + 1U, + }, + [PINCTRL_FUNC_SDIO0_PC] = { + .name = "sdio0_pc", + .regval = 0x08, + .group_base = PINCTRL_GRP_SDIO0_0_PC, + .group_size = PINCTRL_GRP_SDIO0_2_PC - PINCTRL_GRP_SDIO0_0_PC + 1U, + }, + [PINCTRL_FUNC_SDIO0_CD] = { + .name = "sdio0_cd", + .regval = 0x08, + .group_base = PINCTRL_GRP_SDIO0_0_CD, + .group_size = PINCTRL_GRP_SDIO0_2_CD - PINCTRL_GRP_SDIO0_0_CD + 1U, + }, + [PINCTRL_FUNC_SDIO0_WP] = { + .name = "sdio0_wp", + .regval = 0x08, + .group_base = PINCTRL_GRP_SDIO0_0_WP, + .group_size = PINCTRL_GRP_SDIO0_2_WP - PINCTRL_GRP_SDIO0_0_WP + 1U, + }, + [PINCTRL_FUNC_SDIO1] = { + .name = "sdio1", + .regval = 0x10, + .group_base = PINCTRL_GRP_SDIO1_0, + .group_size = PINCTRL_GRP_SDIO1_1BIT_1_3 - PINCTRL_GRP_SDIO1_0 + 1U, + }, + [PINCTRL_FUNC_SDIO1_PC] = { + .name = "sdio1_pc", + .regval = 0x10, + .group_base = PINCTRL_GRP_SDIO1_0_PC, + .group_size = PINCTRL_GRP_SDIO1_1_PC - PINCTRL_GRP_SDIO1_0_PC + 1U, + }, + [PINCTRL_FUNC_SDIO1_CD] = { + .name = "sdio1_cd", + .regval = 0x10, + .group_base = PINCTRL_GRP_SDIO1_0_CD, + .group_size = PINCTRL_GRP_SDIO1_1_CD - PINCTRL_GRP_SDIO1_0_CD + 1U, + }, + [PINCTRL_FUNC_SDIO1_WP] = { + .name = "sdio1_wp", + .regval = 0x10, + .group_base = PINCTRL_GRP_SDIO1_0_WP, + .group_size = PINCTRL_GRP_SDIO1_1_WP - PINCTRL_GRP_SDIO1_0_WP + 1U, + }, + [PINCTRL_FUNC_NAND0] = { + .name = "nand0", + .regval = 0x04, + .group_base = PINCTRL_GRP_NAND0_0, + .group_size = PINCTRL_GRP_NAND0_0 - PINCTRL_GRP_NAND0_0 + 1U, + }, + [PINCTRL_FUNC_NAND0_CE] = { + .name = "nand0_ce", + .regval = 0x04, + .group_base = PINCTRL_GRP_NAND0_0_CE, + .group_size = PINCTRL_GRP_NAND0_1_CE - PINCTRL_GRP_NAND0_0_CE + 1U, + }, + [PINCTRL_FUNC_NAND0_RB] = { + .name = "nand0_rb", + .regval = 0x04, + .group_base = PINCTRL_GRP_NAND0_0_RB, + .group_size = PINCTRL_GRP_NAND0_1_RB - PINCTRL_GRP_NAND0_0_RB + 1U, + }, + [PINCTRL_FUNC_NAND0_DQS] = { + .name = "nand0_dqs", + .regval = 0x04, + .group_base = PINCTRL_GRP_NAND0_0_DQS, + .group_size = PINCTRL_GRP_NAND0_1_DQS - PINCTRL_GRP_NAND0_0_DQS + 1U, + }, + [PINCTRL_FUNC_TTC0_CLK] = { + .name = "ttc0_clk", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC0_0_CLK, + .group_size = PINCTRL_GRP_TTC0_8_CLK - PINCTRL_GRP_TTC0_0_CLK + 1U, + }, + [PINCTRL_FUNC_TTC0_WAV] = { + .name = "ttc0_wav", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC0_0_WAV, + .group_size = PINCTRL_GRP_TTC0_8_WAV - PINCTRL_GRP_TTC0_0_WAV + 1U, + }, + [PINCTRL_FUNC_TTC1_CLK] = { + .name = "ttc1_clk", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC1_0_CLK, + .group_size = PINCTRL_GRP_TTC1_8_CLK - PINCTRL_GRP_TTC1_0_CLK + 1U, + }, + [PINCTRL_FUNC_TTC1_WAV] = { + .name = "ttc1_wav", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC1_0_WAV, + .group_size = PINCTRL_GRP_TTC1_8_WAV - PINCTRL_GRP_TTC1_0_WAV + 1U, + }, + [PINCTRL_FUNC_TTC2_CLK] = { + .name = "ttc2_clk", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC2_0_CLK, + .group_size = PINCTRL_GRP_TTC2_8_CLK - PINCTRL_GRP_TTC2_0_CLK + 1U, + }, + [PINCTRL_FUNC_TTC2_WAV] = { + .name = "ttc2_wav", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC2_0_WAV, + .group_size = PINCTRL_GRP_TTC2_8_WAV - PINCTRL_GRP_TTC2_0_WAV + 1U, + }, + [PINCTRL_FUNC_TTC3_CLK] = { + .name = "ttc3_clk", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC3_0_CLK, + .group_size = PINCTRL_GRP_TTC3_8_CLK - PINCTRL_GRP_TTC3_0_CLK + 1U, + }, + [PINCTRL_FUNC_TTC3_WAV] = { + .name = "ttc3_wav", + .regval = 0xa0, + .group_base = PINCTRL_GRP_TTC3_0_WAV, + .group_size = PINCTRL_GRP_TTC3_8_WAV - PINCTRL_GRP_TTC3_0_WAV + 1U, + }, + [PINCTRL_FUNC_UART0] = { + .name = "uart0", + .regval = 0xc0, + .group_base = PINCTRL_GRP_UART0_0, + .group_size = PINCTRL_GRP_UART0_18 - PINCTRL_GRP_UART0_0 + 1U, + }, + [PINCTRL_FUNC_UART1] = { + .name = "uart1", + .regval = 0xc0, + .group_base = PINCTRL_GRP_UART1_0, + .group_size = PINCTRL_GRP_UART1_18 - PINCTRL_GRP_UART1_0 + 1U, + }, + [PINCTRL_FUNC_USB0] = { + .name = "usb0", + .regval = 0x04, + .group_base = PINCTRL_GRP_USB0_0, + .group_size = PINCTRL_GRP_USB0_0 - PINCTRL_GRP_USB0_0 + 1U, + }, + [PINCTRL_FUNC_USB1] = { + .name = "usb1", + .regval = 0x04, + .group_base = PINCTRL_GRP_USB1_0, + .group_size = PINCTRL_GRP_USB1_0 - PINCTRL_GRP_USB1_0 + 1U, + }, + [PINCTRL_FUNC_SWDT0_CLK] = { + .name = "swdt0_clk", + .regval = 0x60, + .group_base = PINCTRL_GRP_SWDT0_0_CLK, + .group_size = PINCTRL_GRP_SWDT0_12_CLK - PINCTRL_GRP_SWDT0_0_CLK + 1U, + }, + [PINCTRL_FUNC_SWDT0_RST] = { + .name = "swdt0_rst", + .regval = 0x60, + .group_base = PINCTRL_GRP_SWDT0_0_RST, + .group_size = PINCTRL_GRP_SWDT0_12_RST - PINCTRL_GRP_SWDT0_0_RST + 1U, + }, + [PINCTRL_FUNC_SWDT1_CLK] = { + .name = "swdt1_clk", + .regval = 0x60, + .group_base = PINCTRL_GRP_SWDT1_0_CLK, + .group_size = PINCTRL_GRP_SWDT1_12_CLK - PINCTRL_GRP_SWDT1_0_CLK + 1U, + }, + [PINCTRL_FUNC_SWDT1_RST] = { + .name = "swdt1_rst", + .regval = 0x60, + .group_base = PINCTRL_GRP_SWDT1_0_RST, + .group_size = PINCTRL_GRP_SWDT1_12_RST - PINCTRL_GRP_SWDT1_0_RST + 1U, + }, + [PINCTRL_FUNC_PMU0] = { + .name = "pmu0", + .regval = 0x08, + .group_base = PINCTRL_GRP_PMU0_0, + .group_size = PINCTRL_GRP_PMU0_11 - PINCTRL_GRP_PMU0_0 + 1U, + }, + [PINCTRL_FUNC_PCIE0] = { + .name = "pcie0", + .regval = 0x04, + .group_base = PINCTRL_GRP_PCIE0_0, + .group_size = PINCTRL_GRP_PCIE0_7 - PINCTRL_GRP_PCIE0_0 + 1U, + }, + [PINCTRL_FUNC_CSU0] = { + .name = "csu0", + .regval = 0x18, + .group_base = PINCTRL_GRP_CSU0_0, + .group_size = PINCTRL_GRP_CSU0_11 - PINCTRL_GRP_CSU0_0 + 1U, + }, + [PINCTRL_FUNC_DPAUX0] = { + .name = "dpaux0", + .regval = 0x18, + .group_base = PINCTRL_GRP_DPAUX0_0, + .group_size = PINCTRL_GRP_DPAUX0_3 - PINCTRL_GRP_DPAUX0_0 + 1U, + }, + [PINCTRL_FUNC_PJTAG0] = { + .name = "pjtag0", + .regval = 0x60, + .group_base = PINCTRL_GRP_PJTAG0_0, + .group_size = PINCTRL_GRP_PJTAG0_5 - PINCTRL_GRP_PJTAG0_0 + 1U, + }, + [PINCTRL_FUNC_TRACE0] = { + .name = "trace0", + .regval = 0xe0, + .group_base = PINCTRL_GRP_TRACE0_0, + .group_size = PINCTRL_GRP_TRACE0_2 - PINCTRL_GRP_TRACE0_0 + 1U, + }, + [PINCTRL_FUNC_TRACE0_CLK] = { + .name = "trace0_clk", + .regval = 0xe0, + .group_base = PINCTRL_GRP_TRACE0_0_CLK, + .group_size = PINCTRL_GRP_TRACE0_2_CLK - PINCTRL_GRP_TRACE0_0_CLK + 1U, + }, + [PINCTRL_FUNC_TESTSCAN0] = { + .name = "testscan0", + .regval = 0x10, + .group_base = PINCTRL_GRP_TESTSCAN0_0, + .group_size = PINCTRL_GRP_TESTSCAN0_0 - PINCTRL_GRP_TESTSCAN0_0 + 1U, + }, +}; + +static struct zynqmp_pin_group zynqmp_pin_groups[MAX_PIN] = { + [PINCTRL_PIN_0] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_0, + PINCTRL_GRP_CAN1_0, + PINCTRL_GRP_I2C1_0, + PINCTRL_GRP_PJTAG0_0, + PINCTRL_GRP_SPI0_0, + PINCTRL_GRP_TTC3_0_CLK, + PINCTRL_GRP_UART1_0, + PINCTRL_GRP_TRACE0_0_CLK, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_1] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_1, + PINCTRL_GRP_CAN1_0, + PINCTRL_GRP_I2C1_0, + PINCTRL_GRP_PJTAG0_0, + PINCTRL_GRP_SPI0_0_SS2, + PINCTRL_GRP_TTC3_0_WAV, + PINCTRL_GRP_UART1_0, + PINCTRL_GRP_TRACE0_0_CLK, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_2] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_2, + PINCTRL_GRP_CAN0_0, + PINCTRL_GRP_I2C0_0, + PINCTRL_GRP_PJTAG0_0, + PINCTRL_GRP_SPI0_0_SS1, + PINCTRL_GRP_TTC2_0_CLK, + PINCTRL_GRP_UART0_0, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_3] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_3, + PINCTRL_GRP_CAN0_0, + PINCTRL_GRP_I2C0_0, + PINCTRL_GRP_PJTAG0_0, + PINCTRL_GRP_SPI0_0_SS0, + PINCTRL_GRP_TTC2_0_WAV, + PINCTRL_GRP_UART0_0, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_4] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_4, + PINCTRL_GRP_CAN1_1, + PINCTRL_GRP_I2C1_1, + PINCTRL_GRP_SWDT1_0_CLK, + PINCTRL_GRP_SPI0_0, + PINCTRL_GRP_TTC1_0_CLK, + PINCTRL_GRP_UART1_1, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_5] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI_SS, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_5, + PINCTRL_GRP_CAN1_1, + PINCTRL_GRP_I2C1_1, + PINCTRL_GRP_SWDT1_0_RST, + PINCTRL_GRP_SPI0_0, + PINCTRL_GRP_TTC1_0_WAV, + PINCTRL_GRP_UART1_1, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_6] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI_FBCLK, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_6, + PINCTRL_GRP_CAN0_1, + PINCTRL_GRP_I2C0_1, + PINCTRL_GRP_SWDT0_0_CLK, + PINCTRL_GRP_SPI1_0, + PINCTRL_GRP_TTC0_0_CLK, + PINCTRL_GRP_UART0_1, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_7] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI_SS, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_7, + PINCTRL_GRP_CAN0_1, + PINCTRL_GRP_I2C0_1, + PINCTRL_GRP_SWDT0_0_RST, + PINCTRL_GRP_SPI1_0_SS2, + PINCTRL_GRP_TTC0_0_WAV, + PINCTRL_GRP_UART0_1, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_8] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_8, + PINCTRL_GRP_CAN1_2, + PINCTRL_GRP_I2C1_2, + PINCTRL_GRP_SWDT1_1_CLK, + PINCTRL_GRP_SPI1_0_SS1, + PINCTRL_GRP_TTC3_1_CLK, + PINCTRL_GRP_UART1_2, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_9] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_NAND0_0_CE, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_9, + PINCTRL_GRP_CAN1_2, + PINCTRL_GRP_I2C1_2, + PINCTRL_GRP_SWDT1_1_RST, + PINCTRL_GRP_SPI1_0_SS0, + PINCTRL_GRP_TTC3_1_WAV, + PINCTRL_GRP_UART1_2, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_10] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_NAND0_0_RB, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_10, + PINCTRL_GRP_CAN0_2, + PINCTRL_GRP_I2C0_2, + PINCTRL_GRP_SWDT0_1_CLK, + PINCTRL_GRP_SPI1_0, + PINCTRL_GRP_TTC2_1_CLK, + PINCTRL_GRP_UART0_2, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_11] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_NAND0_0_RB, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_11, + PINCTRL_GRP_CAN0_2, + PINCTRL_GRP_I2C0_2, + PINCTRL_GRP_SWDT0_1_RST, + PINCTRL_GRP_SPI1_0, + PINCTRL_GRP_TTC2_1_WAV, + PINCTRL_GRP_UART0_2, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_12] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_NAND0_0_DQS, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_12, + PINCTRL_GRP_CAN1_3, + PINCTRL_GRP_I2C1_3, + PINCTRL_GRP_PJTAG0_1, + PINCTRL_GRP_SPI0_1, + PINCTRL_GRP_TTC1_1_CLK, + PINCTRL_GRP_UART1_3, + PINCTRL_GRP_TRACE0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_13] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_13, + PINCTRL_GRP_CAN1_3, + PINCTRL_GRP_I2C1_3, + PINCTRL_GRP_PJTAG0_1, + PINCTRL_GRP_SPI0_1_SS2, + PINCTRL_GRP_TTC1_1_WAV, + PINCTRL_GRP_UART1_3, + PINCTRL_GRP_TRACE0_0, + PINCTRL_GRP_SDIO0_4BIT_0_0, + PINCTRL_GRP_SDIO0_1BIT_0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_14] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_14, + PINCTRL_GRP_CAN0_3, + PINCTRL_GRP_I2C0_3, + PINCTRL_GRP_PJTAG0_1, + PINCTRL_GRP_SPI0_1_SS1, + PINCTRL_GRP_TTC0_1_CLK, + PINCTRL_GRP_UART0_3, + PINCTRL_GRP_TRACE0_0, + PINCTRL_GRP_SDIO0_4BIT_0_0, + PINCTRL_GRP_SDIO0_1BIT_0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_15] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_15, + PINCTRL_GRP_CAN0_3, + PINCTRL_GRP_I2C0_3, + PINCTRL_GRP_PJTAG0_1, + PINCTRL_GRP_SPI0_1_SS0, + PINCTRL_GRP_TTC0_1_WAV, + PINCTRL_GRP_UART0_3, + PINCTRL_GRP_TRACE0_0, + PINCTRL_GRP_SDIO0_4BIT_0_0, + PINCTRL_GRP_SDIO0_1BIT_0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_16] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_16, + PINCTRL_GRP_CAN1_4, + PINCTRL_GRP_I2C1_4, + PINCTRL_GRP_SWDT1_2_CLK, + PINCTRL_GRP_SPI0_1, + PINCTRL_GRP_TTC3_2_CLK, + PINCTRL_GRP_UART1_4, + PINCTRL_GRP_TRACE0_0, + PINCTRL_GRP_SDIO0_4BIT_0_0, + PINCTRL_GRP_SDIO0_1BIT_0_3, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_17] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_17, + PINCTRL_GRP_CAN1_4, + PINCTRL_GRP_I2C1_4, + PINCTRL_GRP_SWDT1_2_RST, + PINCTRL_GRP_SPI0_1, + PINCTRL_GRP_TTC3_2_WAV, + PINCTRL_GRP_UART1_4, + PINCTRL_GRP_TRACE0_0, + PINCTRL_GRP_SDIO0_4BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_4, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_18] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_0, + PINCTRL_GRP_GPIO0_18, + PINCTRL_GRP_CAN0_4, + PINCTRL_GRP_I2C0_4, + PINCTRL_GRP_SWDT0_2_CLK, + PINCTRL_GRP_SPI1_1, + PINCTRL_GRP_TTC2_2_CLK, + PINCTRL_GRP_UART0_4, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_5, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_19] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_1, + PINCTRL_GRP_GPIO0_19, + PINCTRL_GRP_CAN0_4, + PINCTRL_GRP_I2C0_4, + PINCTRL_GRP_SWDT0_2_RST, + PINCTRL_GRP_SPI1_1_SS2, + PINCTRL_GRP_TTC2_2_WAV, + PINCTRL_GRP_UART0_4, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_6, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_20] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_2, + PINCTRL_GRP_GPIO0_20, + PINCTRL_GRP_CAN1_5, + PINCTRL_GRP_I2C1_5, + PINCTRL_GRP_SWDT1_3_CLK, + PINCTRL_GRP_SPI1_1_SS1, + PINCTRL_GRP_TTC1_2_CLK, + PINCTRL_GRP_UART1_5, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_21] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_3, + PINCTRL_GRP_GPIO0_21, + PINCTRL_GRP_CAN1_5, + PINCTRL_GRP_I2C1_5, + PINCTRL_GRP_SWDT1_3_RST, + PINCTRL_GRP_SPI1_1_SS0, + PINCTRL_GRP_TTC1_2_WAV, + PINCTRL_GRP_UART1_5, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_0_0, + PINCTRL_GRP_SDIO0_4BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_0, + PINCTRL_GRP_SDIO0_1BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_2, + PINCTRL_GRP_SDIO0_1BIT_0_3, + PINCTRL_GRP_SDIO0_1BIT_0_4, + PINCTRL_GRP_SDIO0_1BIT_0_5, + PINCTRL_GRP_SDIO0_1BIT_0_6, + PINCTRL_GRP_SDIO0_1BIT_0_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_22] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_4, + PINCTRL_GRP_GPIO0_22, + PINCTRL_GRP_CAN0_5, + PINCTRL_GRP_I2C0_5, + PINCTRL_GRP_SWDT0_3_CLK, + PINCTRL_GRP_SPI1_1, + PINCTRL_GRP_TTC0_2_CLK, + PINCTRL_GRP_UART0_5, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_0_0, + PINCTRL_GRP_SDIO0_4BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_0, + PINCTRL_GRP_SDIO0_1BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_2, + PINCTRL_GRP_SDIO0_1BIT_0_3, + PINCTRL_GRP_SDIO0_1BIT_0_4, + PINCTRL_GRP_SDIO0_1BIT_0_5, + PINCTRL_GRP_SDIO0_1BIT_0_6, + PINCTRL_GRP_SDIO0_1BIT_0_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_23] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0_PC, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_5, + PINCTRL_GRP_GPIO0_23, + PINCTRL_GRP_CAN0_5, + PINCTRL_GRP_I2C0_5, + PINCTRL_GRP_SWDT0_3_RST, + PINCTRL_GRP_SPI1_1, + PINCTRL_GRP_TTC0_2_WAV, + PINCTRL_GRP_UART0_5, + PINCTRL_GRP_RESERVED, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_24] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0_CD, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_6, + PINCTRL_GRP_GPIO0_24, + PINCTRL_GRP_CAN1_6, + PINCTRL_GRP_I2C1_6, + PINCTRL_GRP_SWDT1_4_CLK, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TTC3_3_CLK, + PINCTRL_GRP_UART1_6, + PINCTRL_GRP_RESERVED, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_25] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_SDIO0_0_WP, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_7, + PINCTRL_GRP_GPIO0_25, + PINCTRL_GRP_CAN1_6, + PINCTRL_GRP_I2C1_6, + PINCTRL_GRP_SWDT1_4_RST, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_TTC3_3_WAV, + PINCTRL_GRP_UART1_6, + PINCTRL_GRP_RESERVED, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_26] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_GEMTSU0_0, + PINCTRL_GRP_NAND0_1_CE, + PINCTRL_GRP_PMU0_0, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_8, + PINCTRL_GRP_GPIO0_26, + PINCTRL_GRP_CAN0_6, + PINCTRL_GRP_I2C0_6, + PINCTRL_GRP_PJTAG0_2, + PINCTRL_GRP_SPI0_2, + PINCTRL_GRP_TTC2_3_CLK, + PINCTRL_GRP_UART0_6, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_27] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_NAND0_1_RB, + PINCTRL_GRP_PMU0_1, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_0, + PINCTRL_GRP_GPIO0_27, + PINCTRL_GRP_CAN0_6, + PINCTRL_GRP_I2C0_6, + PINCTRL_GRP_PJTAG0_2, + PINCTRL_GRP_SPI0_2_SS2, + PINCTRL_GRP_TTC2_3_WAV, + PINCTRL_GRP_UART0_6, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_28] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_NAND0_1_RB, + PINCTRL_GRP_PMU0_2, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_0, + PINCTRL_GRP_GPIO0_28, + PINCTRL_GRP_CAN1_7, + PINCTRL_GRP_I2C1_7, + PINCTRL_GRP_PJTAG0_2, + PINCTRL_GRP_SPI0_2_SS1, + PINCTRL_GRP_TTC1_3_CLK, + PINCTRL_GRP_UART1_7, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_29] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_0, + PINCTRL_GRP_PMU0_3, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_1, + PINCTRL_GRP_GPIO0_29, + PINCTRL_GRP_CAN1_7, + PINCTRL_GRP_I2C1_7, + PINCTRL_GRP_PJTAG0_2, + PINCTRL_GRP_SPI0_2_SS0, + PINCTRL_GRP_TTC1_3_WAV, + PINCTRL_GRP_UART1_7, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_30] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_1, + PINCTRL_GRP_PMU0_4, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_1, + PINCTRL_GRP_GPIO0_30, + PINCTRL_GRP_CAN0_7, + PINCTRL_GRP_I2C0_7, + PINCTRL_GRP_SWDT0_4_CLK, + PINCTRL_GRP_SPI0_2, + PINCTRL_GRP_TTC0_3_CLK, + PINCTRL_GRP_UART0_7, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_31] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_2, + PINCTRL_GRP_PMU0_5, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_9, + PINCTRL_GRP_GPIO0_31, + PINCTRL_GRP_CAN0_7, + PINCTRL_GRP_I2C0_7, + PINCTRL_GRP_SWDT0_4_RST, + PINCTRL_GRP_SPI0_2, + PINCTRL_GRP_TTC0_3_WAV, + PINCTRL_GRP_UART0_7, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_32] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_NAND0_1_DQS, + PINCTRL_GRP_PMU0_6, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_10, + PINCTRL_GRP_GPIO0_32, + PINCTRL_GRP_CAN1_8, + PINCTRL_GRP_I2C1_8, + PINCTRL_GRP_SWDT1_5_CLK, + PINCTRL_GRP_SPI1_2, + PINCTRL_GRP_TTC3_4_CLK, + PINCTRL_GRP_UART1_8, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_33] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_3, + PINCTRL_GRP_PMU0_7, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_CSU0_11, + PINCTRL_GRP_GPIO0_33, + PINCTRL_GRP_CAN1_8, + PINCTRL_GRP_I2C1_8, + PINCTRL_GRP_SWDT1_5_RST, + PINCTRL_GRP_SPI1_2_SS2, + PINCTRL_GRP_TTC3_4_WAV, + PINCTRL_GRP_UART1_8, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_34] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_4, + PINCTRL_GRP_PMU0_8, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_2, + PINCTRL_GRP_GPIO0_34, + PINCTRL_GRP_CAN0_8, + PINCTRL_GRP_I2C0_8, + PINCTRL_GRP_SWDT0_5_CLK, + PINCTRL_GRP_SPI1_2_SS1, + PINCTRL_GRP_TTC2_4_CLK, + PINCTRL_GRP_UART0_8, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_35] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_5, + PINCTRL_GRP_PMU0_9, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_2, + PINCTRL_GRP_GPIO0_35, + PINCTRL_GRP_CAN0_8, + PINCTRL_GRP_I2C0_8, + PINCTRL_GRP_SWDT0_5_RST, + PINCTRL_GRP_SPI1_2_SS0, + PINCTRL_GRP_TTC2_4_WAV, + PINCTRL_GRP_UART0_8, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_36] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_6, + PINCTRL_GRP_PMU0_10, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_3, + PINCTRL_GRP_GPIO0_36, + PINCTRL_GRP_CAN1_9, + PINCTRL_GRP_I2C1_9, + PINCTRL_GRP_SWDT1_6_CLK, + PINCTRL_GRP_SPI1_2, + PINCTRL_GRP_TTC1_4_CLK, + PINCTRL_GRP_UART1_9, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_37] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_PCIE0_7, + PINCTRL_GRP_PMU0_11, + PINCTRL_GRP_TESTSCAN0_0, + PINCTRL_GRP_DPAUX0_3, + PINCTRL_GRP_GPIO0_37, + PINCTRL_GRP_CAN1_9, + PINCTRL_GRP_I2C1_9, + PINCTRL_GRP_SWDT1_6_RST, + PINCTRL_GRP_SPI1_2, + PINCTRL_GRP_TTC1_4_WAV, + PINCTRL_GRP_UART1_9, + PINCTRL_GRP_TRACE0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_38] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_38, + PINCTRL_GRP_CAN0_9, + PINCTRL_GRP_I2C0_9, + PINCTRL_GRP_PJTAG0_3, + PINCTRL_GRP_SPI0_3, + PINCTRL_GRP_TTC0_4_CLK, + PINCTRL_GRP_UART0_9, + PINCTRL_GRP_TRACE0_1_CLK, + PINCTRL_GRP_SDIO0_4BIT_1_0, + PINCTRL_GRP_SDIO0_4BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_0, + PINCTRL_GRP_SDIO0_1BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_2, + PINCTRL_GRP_SDIO0_1BIT_1_3, + PINCTRL_GRP_SDIO0_1BIT_1_4, + PINCTRL_GRP_SDIO0_1BIT_1_5, + PINCTRL_GRP_SDIO0_1BIT_1_6, + PINCTRL_GRP_SDIO0_1BIT_1_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_39] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1_CD, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_39, + PINCTRL_GRP_CAN0_9, + PINCTRL_GRP_I2C0_9, + PINCTRL_GRP_PJTAG0_3, + PINCTRL_GRP_SPI0_3_SS2, + PINCTRL_GRP_TTC0_4_WAV, + PINCTRL_GRP_UART0_9, + PINCTRL_GRP_TRACE0_1_CLK, + PINCTRL_GRP_SDIO1_4BIT_0_0, + PINCTRL_GRP_SDIO1_1BIT_0_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_40] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_40, + PINCTRL_GRP_CAN1_10, + PINCTRL_GRP_I2C1_10, + PINCTRL_GRP_PJTAG0_3, + PINCTRL_GRP_SPI0_3_SS1, + PINCTRL_GRP_TTC3_5_CLK, + PINCTRL_GRP_UART1_10, + PINCTRL_GRP_TRACE0_1, + PINCTRL_GRP_SDIO0_4BIT_1_0, + PINCTRL_GRP_SDIO0_4BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_0, + PINCTRL_GRP_SDIO0_1BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_2, + PINCTRL_GRP_SDIO0_1BIT_1_3, + PINCTRL_GRP_SDIO0_1BIT_1_4, + PINCTRL_GRP_SDIO0_1BIT_1_5, + PINCTRL_GRP_SDIO0_1BIT_1_6, + PINCTRL_GRP_SDIO0_1BIT_1_7, + PINCTRL_GRP_SDIO1_4BIT_0_0, + PINCTRL_GRP_SDIO1_1BIT_0_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_41] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_41, + PINCTRL_GRP_CAN1_10, + PINCTRL_GRP_I2C1_10, + PINCTRL_GRP_PJTAG0_3, + PINCTRL_GRP_SPI0_3_SS0, + PINCTRL_GRP_TTC3_5_WAV, + PINCTRL_GRP_UART1_10, + PINCTRL_GRP_TRACE0_1, + PINCTRL_GRP_SDIO0_4BIT_1_0, + PINCTRL_GRP_SDIO0_1BIT_1_0, + PINCTRL_GRP_SDIO1_4BIT_0_0, + PINCTRL_GRP_SDIO1_1BIT_0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_42] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_42, + PINCTRL_GRP_CAN0_10, + PINCTRL_GRP_I2C0_10, + PINCTRL_GRP_SWDT0_6_CLK, + PINCTRL_GRP_SPI0_3, + PINCTRL_GRP_TTC2_5_CLK, + PINCTRL_GRP_UART0_10, + PINCTRL_GRP_TRACE0_1, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO0_4BIT_1_0, + PINCTRL_GRP_SDIO0_1BIT_1_1, + PINCTRL_GRP_SDIO1_4BIT_0_0, + PINCTRL_GRP_SDIO1_1BIT_0_3, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_43] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0_PC, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_43, + PINCTRL_GRP_CAN0_10, + PINCTRL_GRP_I2C0_10, + PINCTRL_GRP_SWDT0_6_RST, + PINCTRL_GRP_SPI0_3, + PINCTRL_GRP_TTC2_5_WAV, + PINCTRL_GRP_UART0_10, + PINCTRL_GRP_TRACE0_1, + PINCTRL_GRP_SDIO0_4BIT_1_0, + PINCTRL_GRP_SDIO0_1BIT_1_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_44] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0_WP, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_44, + PINCTRL_GRP_CAN1_11, + PINCTRL_GRP_I2C1_11, + PINCTRL_GRP_SWDT1_7_CLK, + PINCTRL_GRP_SPI1_3, + PINCTRL_GRP_TTC1_5_CLK, + PINCTRL_GRP_UART1_11, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_1_0, + PINCTRL_GRP_SDIO0_1BIT_1_3, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_45] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0_CD, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_45, + PINCTRL_GRP_CAN1_11, + PINCTRL_GRP_I2C1_11, + PINCTRL_GRP_SWDT1_7_RST, + PINCTRL_GRP_SPI1_3_SS2, + PINCTRL_GRP_TTC1_5_WAV, + PINCTRL_GRP_UART1_11, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_4, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_46] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_46, + PINCTRL_GRP_CAN0_11, + PINCTRL_GRP_I2C0_11, + PINCTRL_GRP_SWDT0_7_CLK, + PINCTRL_GRP_SPI1_3_SS1, + PINCTRL_GRP_TTC0_5_CLK, + PINCTRL_GRP_UART0_11, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_5, + PINCTRL_GRP_SDIO1_4BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_4, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_47] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_47, + PINCTRL_GRP_CAN0_11, + PINCTRL_GRP_I2C0_11, + PINCTRL_GRP_SWDT0_7_RST, + PINCTRL_GRP_SPI1_3_SS0, + PINCTRL_GRP_TTC0_5_WAV, + PINCTRL_GRP_UART0_11, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_6, + PINCTRL_GRP_SDIO1_4BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_5, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_48] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_48, + PINCTRL_GRP_CAN1_12, + PINCTRL_GRP_I2C1_12, + PINCTRL_GRP_SWDT1_8_CLK, + PINCTRL_GRP_SPI1_3, + PINCTRL_GRP_TTC3_6_CLK, + PINCTRL_GRP_UART1_12, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_7, + PINCTRL_GRP_SDIO1_4BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_6, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_49] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1_PC, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_49, + PINCTRL_GRP_CAN1_12, + PINCTRL_GRP_I2C1_12, + PINCTRL_GRP_SWDT1_8_RST, + PINCTRL_GRP_SPI1_3, + PINCTRL_GRP_TTC3_6_WAV, + PINCTRL_GRP_UART1_12, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO1_4BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_50] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_GEMTSU0_1, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_1_WP, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_50, + PINCTRL_GRP_CAN0_12, + PINCTRL_GRP_I2C0_12, + PINCTRL_GRP_SWDT0_8_CLK, + PINCTRL_GRP_MDIO1_0, + PINCTRL_GRP_TTC2_6_CLK, + PINCTRL_GRP_UART0_12, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO1_4BIT_0_0, + PINCTRL_GRP_SDIO1_4BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_0, + PINCTRL_GRP_SDIO1_1BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_2, + PINCTRL_GRP_SDIO1_1BIT_0_3, + PINCTRL_GRP_SDIO1_1BIT_0_4, + PINCTRL_GRP_SDIO1_1BIT_0_5, + PINCTRL_GRP_SDIO1_1BIT_0_6, + PINCTRL_GRP_SDIO1_1BIT_0_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_51] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_GEMTSU0_2, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_51, + PINCTRL_GRP_CAN0_12, + PINCTRL_GRP_I2C0_12, + PINCTRL_GRP_SWDT0_8_RST, + PINCTRL_GRP_MDIO1_0, + PINCTRL_GRP_TTC2_6_WAV, + PINCTRL_GRP_UART0_12, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO1_4BIT_0_0, + PINCTRL_GRP_SDIO1_4BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_0, + PINCTRL_GRP_SDIO1_1BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_2, + PINCTRL_GRP_SDIO1_1BIT_0_3, + PINCTRL_GRP_SDIO1_1BIT_0_4, + PINCTRL_GRP_SDIO1_1BIT_0_5, + PINCTRL_GRP_SDIO1_1BIT_0_6, + PINCTRL_GRP_SDIO1_1BIT_0_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_52] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_52, + PINCTRL_GRP_CAN1_13, + PINCTRL_GRP_I2C1_13, + PINCTRL_GRP_PJTAG0_4, + PINCTRL_GRP_SPI0_4, + PINCTRL_GRP_TTC1_6_CLK, + PINCTRL_GRP_UART1_13, + PINCTRL_GRP_TRACE0_2_CLK, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_53] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_53, + PINCTRL_GRP_CAN1_13, + PINCTRL_GRP_I2C1_13, + PINCTRL_GRP_PJTAG0_4, + PINCTRL_GRP_SPI0_4_SS2, + PINCTRL_GRP_TTC1_6_WAV, + PINCTRL_GRP_UART1_13, + PINCTRL_GRP_TRACE0_2_CLK, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_54] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_54, + PINCTRL_GRP_CAN0_13, + PINCTRL_GRP_I2C0_13, + PINCTRL_GRP_PJTAG0_4, + PINCTRL_GRP_SPI0_4_SS1, + PINCTRL_GRP_TTC0_6_CLK, + PINCTRL_GRP_UART0_13, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_55] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_55, + PINCTRL_GRP_CAN0_13, + PINCTRL_GRP_I2C0_13, + PINCTRL_GRP_PJTAG0_4, + PINCTRL_GRP_SPI0_4_SS0, + PINCTRL_GRP_TTC0_6_WAV, + PINCTRL_GRP_UART0_13, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_56] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_56, + PINCTRL_GRP_CAN1_14, + PINCTRL_GRP_I2C1_14, + PINCTRL_GRP_SWDT1_9_CLK, + PINCTRL_GRP_SPI0_4, + PINCTRL_GRP_TTC3_7_CLK, + PINCTRL_GRP_UART1_14, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_57] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_57, + PINCTRL_GRP_CAN1_14, + PINCTRL_GRP_I2C1_14, + PINCTRL_GRP_SWDT1_9_RST, + PINCTRL_GRP_SPI0_4, + PINCTRL_GRP_TTC3_7_WAV, + PINCTRL_GRP_UART1_14, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_58] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_58, + PINCTRL_GRP_CAN0_14, + PINCTRL_GRP_I2C0_14, + PINCTRL_GRP_PJTAG0_5, + PINCTRL_GRP_SPI1_4, + PINCTRL_GRP_TTC2_7_CLK, + PINCTRL_GRP_UART0_14, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_59] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_59, + PINCTRL_GRP_CAN0_14, + PINCTRL_GRP_I2C0_14, + PINCTRL_GRP_PJTAG0_5, + PINCTRL_GRP_SPI1_4_SS2, + PINCTRL_GRP_TTC2_7_WAV, + PINCTRL_GRP_UART0_14, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_60] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_60, + PINCTRL_GRP_CAN1_15, + PINCTRL_GRP_I2C1_15, + PINCTRL_GRP_PJTAG0_5, + PINCTRL_GRP_SPI1_4_SS1, + PINCTRL_GRP_TTC1_7_CLK, + PINCTRL_GRP_UART1_15, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_61] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_61, + PINCTRL_GRP_CAN1_15, + PINCTRL_GRP_I2C1_15, + PINCTRL_GRP_PJTAG0_5, + PINCTRL_GRP_SPI1_4_SS0, + PINCTRL_GRP_TTC1_7_WAV, + PINCTRL_GRP_UART1_15, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_62] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_62, + PINCTRL_GRP_CAN0_15, + PINCTRL_GRP_I2C0_15, + PINCTRL_GRP_SWDT0_9_CLK, + PINCTRL_GRP_SPI1_4, + PINCTRL_GRP_TTC0_7_CLK, + PINCTRL_GRP_UART0_15, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_63] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_63, + PINCTRL_GRP_CAN0_15, + PINCTRL_GRP_I2C0_15, + PINCTRL_GRP_SWDT0_9_RST, + PINCTRL_GRP_SPI1_4, + PINCTRL_GRP_TTC0_7_WAV, + PINCTRL_GRP_UART0_15, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_64] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_64, + PINCTRL_GRP_CAN1_16, + PINCTRL_GRP_I2C1_16, + PINCTRL_GRP_SWDT1_10_CLK, + PINCTRL_GRP_SPI0_5, + PINCTRL_GRP_TTC3_8_CLK, + PINCTRL_GRP_UART1_16, + PINCTRL_GRP_TRACE0_2, + PINCTRL_GRP_SDIO0_4BIT_2_0, + PINCTRL_GRP_SDIO0_4BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_0, + PINCTRL_GRP_SDIO0_1BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_2, + PINCTRL_GRP_SDIO0_1BIT_2_3, + PINCTRL_GRP_SDIO0_1BIT_2_4, + PINCTRL_GRP_SDIO0_1BIT_2_5, + PINCTRL_GRP_SDIO0_1BIT_2_6, + PINCTRL_GRP_SDIO0_1BIT_2_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_65] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2_CD, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_65, + PINCTRL_GRP_CAN1_16, + PINCTRL_GRP_I2C1_16, + PINCTRL_GRP_SWDT1_10_RST, + PINCTRL_GRP_SPI0_5_SS2, + PINCTRL_GRP_TTC3_8_WAV, + PINCTRL_GRP_UART1_16, + PINCTRL_GRP_TRACE0_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_66] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_66, + PINCTRL_GRP_CAN0_16, + PINCTRL_GRP_I2C0_16, + PINCTRL_GRP_SWDT0_10_CLK, + PINCTRL_GRP_SPI0_5_SS1, + PINCTRL_GRP_TTC2_8_CLK, + PINCTRL_GRP_UART0_16, + PINCTRL_GRP_TRACE0_2, + PINCTRL_GRP_SDIO0_4BIT_2_0, + PINCTRL_GRP_SDIO0_4BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_0, + PINCTRL_GRP_SDIO0_1BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_2, + PINCTRL_GRP_SDIO0_1BIT_2_3, + PINCTRL_GRP_SDIO0_1BIT_2_4, + PINCTRL_GRP_SDIO0_1BIT_2_5, + PINCTRL_GRP_SDIO0_1BIT_2_6, + PINCTRL_GRP_SDIO0_1BIT_2_7, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_67] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_67, + PINCTRL_GRP_CAN0_16, + PINCTRL_GRP_I2C0_16, + PINCTRL_GRP_SWDT0_10_RST, + PINCTRL_GRP_SPI0_5_SS0, + PINCTRL_GRP_TTC2_8_WAV, + PINCTRL_GRP_UART0_16, + PINCTRL_GRP_TRACE0_2, + PINCTRL_GRP_SDIO0_4BIT_2_0, + PINCTRL_GRP_SDIO0_1BIT_2_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_68] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_68, + PINCTRL_GRP_CAN1_17, + PINCTRL_GRP_I2C1_17, + PINCTRL_GRP_SWDT1_11_CLK, + PINCTRL_GRP_SPI0_5, + PINCTRL_GRP_TTC1_8_CLK, + PINCTRL_GRP_UART1_17, + PINCTRL_GRP_TRACE0_2, + PINCTRL_GRP_SDIO0_4BIT_2_0, + PINCTRL_GRP_SDIO0_1BIT_2_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_69] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO1_1_WP, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_69, + PINCTRL_GRP_CAN1_17, + PINCTRL_GRP_I2C1_17, + PINCTRL_GRP_SWDT1_11_RST, + PINCTRL_GRP_SPI0_5, + PINCTRL_GRP_TTC1_8_WAV, + PINCTRL_GRP_UART1_17, + PINCTRL_GRP_TRACE0_2, + PINCTRL_GRP_SDIO0_4BIT_2_0, + PINCTRL_GRP_SDIO0_1BIT_2_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_70] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO1_1_PC, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_70, + PINCTRL_GRP_CAN0_17, + PINCTRL_GRP_I2C0_17, + PINCTRL_GRP_SWDT0_11_CLK, + PINCTRL_GRP_SPI1_5, + PINCTRL_GRP_TTC0_8_CLK, + PINCTRL_GRP_UART0_17, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_2_0, + PINCTRL_GRP_SDIO0_1BIT_2_3, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_71] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO1_4BIT_1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_71, + PINCTRL_GRP_CAN0_17, + PINCTRL_GRP_I2C0_17, + PINCTRL_GRP_SWDT0_11_RST, + PINCTRL_GRP_SPI1_5_SS2, + PINCTRL_GRP_TTC0_8_WAV, + PINCTRL_GRP_UART0_17, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO0_4BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_4, + PINCTRL_GRP_SDIO1_1BIT_1_0, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_72] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO1_4BIT_1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_72, + PINCTRL_GRP_CAN1_18, + PINCTRL_GRP_I2C1_18, + PINCTRL_GRP_SWDT1_12_CLK, + PINCTRL_GRP_SPI1_5_SS1, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_UART1_18, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_5, + PINCTRL_GRP_SDIO1_1BIT_1_1, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_73] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO1_4BIT_1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_73, + PINCTRL_GRP_CAN1_18, + PINCTRL_GRP_I2C1_18, + PINCTRL_GRP_SWDT1_12_RST, + PINCTRL_GRP_SPI1_5_SS0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_UART1_18, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_6, + PINCTRL_GRP_SDIO1_1BIT_1_2, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_74] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO1_4BIT_1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_74, + PINCTRL_GRP_CAN0_18, + PINCTRL_GRP_I2C0_18, + PINCTRL_GRP_SWDT0_12_CLK, + PINCTRL_GRP_SPI1_5, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_UART0_18, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_4BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_7, + PINCTRL_GRP_SDIO1_1BIT_1_3, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_75] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_SDIO0_2_PC, + PINCTRL_GRP_SDIO1_4BIT_1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_75, + PINCTRL_GRP_CAN0_18, + PINCTRL_GRP_I2C0_18, + PINCTRL_GRP_SWDT0_12_RST, + PINCTRL_GRP_SPI1_5, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_UART0_18, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO1_1BIT_1_0, + PINCTRL_GRP_SDIO1_1BIT_1_1, + PINCTRL_GRP_SDIO1_1BIT_1_2, + PINCTRL_GRP_SDIO1_1BIT_1_3, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_76] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO0_2_WP, + PINCTRL_GRP_SDIO1_4BIT_1_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_76, + PINCTRL_GRP_CAN1_19, + PINCTRL_GRP_I2C1_19, + PINCTRL_GRP_MDIO0_0, + PINCTRL_GRP_MDIO1_1, + PINCTRL_GRP_MDIO2_0, + PINCTRL_GRP_MDIO3_0, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO1_1BIT_1_0, + PINCTRL_GRP_SDIO1_1BIT_1_1, + PINCTRL_GRP_SDIO1_1BIT_1_2, + PINCTRL_GRP_SDIO1_1BIT_1_3, + END_OF_GROUPS, + }), + }, + [PINCTRL_PIN_77] = { + .groups = &((uint16_t []) { + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_SDIO1_1_CD, + PINCTRL_GRP_RESERVED, + PINCTRL_GRP_GPIO0_77, + PINCTRL_GRP_CAN1_19, + PINCTRL_GRP_I2C1_19, + PINCTRL_GRP_MDIO0_0, + PINCTRL_GRP_MDIO1_1, + PINCTRL_GRP_MDIO2_0, + PINCTRL_GRP_MDIO3_0, + PINCTRL_GRP_RESERVED, + END_OF_GROUPS, + }), + }, +}; + +/** + * pm_api_pinctrl_get_num_pins() - PM call to request number of pins. + * @npins: Number of pins. + * + * This function is used by master to get number of pins. + * + * Return: Returns success. + * + */ +enum pm_ret_status pm_api_pinctrl_get_num_pins(uint32_t *npins) +{ + *npins = MAX_PIN; + + return PM_RET_SUCCESS; +} + +/** + * pm_api_pinctrl_get_num_functions() - PM call to request number of functions. + * @nfuncs: Number of functions. + * + * This function is used by master to get number of functions. + * + * Return: Returns success. + * + */ +enum pm_ret_status pm_api_pinctrl_get_num_functions(uint32_t *nfuncs) +{ + *nfuncs = MAX_FUNCTION; + + return PM_RET_SUCCESS; +} + +/** + * pm_api_pinctrl_get_num_func_groups() - PM call to request number of + * function groups. + * @fid: Function Id. + * @ngroups: Number of function groups. + * + * This function is used by master to get number of function groups. + * + * Return: Returns success. + * + */ +enum pm_ret_status pm_api_pinctrl_get_num_func_groups(uint32_t fid, + uint32_t *ngroups) +{ + if (fid >= MAX_FUNCTION) { + return PM_RET_ERROR_ARGS; + } + + *ngroups = pinctrl_functions[fid].group_size; + + return PM_RET_SUCCESS; +} + +/** + * pm_api_pinctrl_get_function_name() - PM call to request a function name. + * @fid: Function ID. + * @name: Name of function (max 16 bytes). + * + * This function is used by master to get name of function specified + * by given function ID. + * + */ +void pm_api_pinctrl_get_function_name(uint32_t fid, char *name) +{ + if (fid >= MAX_FUNCTION) { + memcpy(name, END_OF_FUNCTION, FUNCTION_NAME_LEN); + } else { + memcpy(name, pinctrl_functions[fid].name, FUNCTION_NAME_LEN); + } +} + +/** + * pm_api_pinctrl_get_function_groups() - PM call to request first 6 function + * groups of function Id. + * @fid: Function ID. + * @index: Index of next function groups. + * @groups: Function groups. + * + * This function is used by master to get function groups specified + * by given function Id. This API will return 6 function groups with + * a single response. To get other function groups, master should call + * same API in loop with new function groups index till error is returned. + * + * E.g First call should have index 0 which will return function groups + * 0, 1, 2, 3, 4 and 5. Next call, index should be 6 which will return + * function groups 6, 7, 8, 9, 10 and 11 and so on. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_pinctrl_get_function_groups(uint32_t fid, + uint32_t index, + uint16_t *groups) +{ + uint16_t grps; + uint16_t end_of_grp_offset; + uint16_t i; + + if (fid >= MAX_FUNCTION) { + return PM_RET_ERROR_ARGS; + } + + memset(groups, END_OF_GROUPS, GROUPS_PAYLOAD_LEN); + + grps = pinctrl_functions[fid].group_base; + end_of_grp_offset = grps + pinctrl_functions[fid].group_size; + + for (i = 0U; i < NUM_GROUPS_PER_RESP; i++) { + if ((grps + index + i) >= end_of_grp_offset) { + break; + } + groups[i] = (grps + index + i); + } + + return PM_RET_SUCCESS; +} + +/** + * pm_api_pinctrl_get_pin_groups() - PM call to request first 6 pin + * groups of pin. + * @pin: Pin. + * @index: Index of next pin groups. + * @groups: pin groups. + * + * This function is used by master to get pin groups specified + * by given pin Id. This API will return 6 pin groups with + * a single response. To get other pin groups, master should call + * same API in loop with new pin groups index till error is returned. + * + * E.g First call should have index 0 which will return pin groups + * 0, 1, 2, 3, 4 and 5. Next call, index should be 6 which will return + * pin groups 6, 7, 8, 9, 10 and 11 and so on. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_api_pinctrl_get_pin_groups(uint32_t pin, + uint32_t index, + uint16_t *groups) +{ + uint32_t i; + uint16_t *grps; + + if (pin >= MAX_PIN) { + return PM_RET_ERROR_ARGS; + } + + memset(groups, END_OF_GROUPS, GROUPS_PAYLOAD_LEN); + + grps = *zynqmp_pin_groups[pin].groups; + if (grps == NULL) { + return PM_RET_SUCCESS; + } + + /* Skip groups till index */ + for (i = 0; i < index; i++) { + if (grps[i] == (uint16_t)END_OF_GROUPS) { + return PM_RET_SUCCESS; + } + } + + for (i = 0; i < NUM_GROUPS_PER_RESP; i++) { + groups[i] = grps[index + i]; + if (groups[i] == (uint16_t)END_OF_GROUPS) { + break; + } + } + + return PM_RET_SUCCESS; +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.h b/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.h new file mode 100644 index 0000000..277af4b --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.h @@ -0,0 +1,723 @@ +/* + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * ZynqMP system level PM-API functions for pin control. + */ + +#ifndef PM_API_PINCTRL_H +#define PM_API_PINCTRL_H + +#include "pm_common.h" + +#define FUNCTION_NAME_LEN (16U) +#define GROUPS_PAYLOAD_LEN (12U) +#define NUM_GROUPS_PER_RESP (6U) +#define END_OF_FUNCTION "END_OF_FUNCTION" +#define END_OF_GROUPS -1 +#define PINCTRL_GRP_RESERVED -2 + +//pinctrl function ids +enum { + PINCTRL_FUNC_CAN0 = (0U), + PINCTRL_FUNC_CAN1 = (1U), + PINCTRL_FUNC_ETHERNET0 = (2U), + PINCTRL_FUNC_ETHERNET1 = (3U), + PINCTRL_FUNC_ETHERNET2 = (4U), + PINCTRL_FUNC_ETHERNET3 = (5U), + PINCTRL_FUNC_GEMTSU0 = (6U), + PINCTRL_FUNC_GPIO0 = (7U), + PINCTRL_FUNC_I2C0 = (8U), + PINCTRL_FUNC_I2C1 = (9U), + PINCTRL_FUNC_MDIO0 = (10U), + PINCTRL_FUNC_MDIO1 = (11U), + PINCTRL_FUNC_MDIO2 = (12U), + PINCTRL_FUNC_MDIO3 = (13U), + PINCTRL_FUNC_QSPI0 = (14U), + PINCTRL_FUNC_QSPI_FBCLK = (15U), + PINCTRL_FUNC_QSPI_SS = (16U), + PINCTRL_FUNC_SPI0 = (17U), + PINCTRL_FUNC_SPI1 = (18U), + PINCTRL_FUNC_SPI0_SS = (19U), + PINCTRL_FUNC_SPI1_SS = (20U), + PINCTRL_FUNC_SDIO0 = (21U), + PINCTRL_FUNC_SDIO0_PC = (22U), + PINCTRL_FUNC_SDIO0_CD = (23U), + PINCTRL_FUNC_SDIO0_WP = (24U), + PINCTRL_FUNC_SDIO1 = (25U), + PINCTRL_FUNC_SDIO1_PC = (26U), + PINCTRL_FUNC_SDIO1_CD = (27U), + PINCTRL_FUNC_SDIO1_WP = (28U), + PINCTRL_FUNC_NAND0 = (29U), + PINCTRL_FUNC_NAND0_CE = (30U), + PINCTRL_FUNC_NAND0_RB = (31U), + PINCTRL_FUNC_NAND0_DQS = (32U), + PINCTRL_FUNC_TTC0_CLK = (33U), + PINCTRL_FUNC_TTC0_WAV = (34U), + PINCTRL_FUNC_TTC1_CLK = (35U), + PINCTRL_FUNC_TTC1_WAV = (36U), + PINCTRL_FUNC_TTC2_CLK = (37U), + PINCTRL_FUNC_TTC2_WAV = (38U), + PINCTRL_FUNC_TTC3_CLK = (39U), + PINCTRL_FUNC_TTC3_WAV = (40U), + PINCTRL_FUNC_UART0 = (41U), + PINCTRL_FUNC_UART1 = (42U), + PINCTRL_FUNC_USB0 = (43U), + PINCTRL_FUNC_USB1 = (44U), + PINCTRL_FUNC_SWDT0_CLK = (45U), + PINCTRL_FUNC_SWDT0_RST = (46U), + PINCTRL_FUNC_SWDT1_CLK = (47U), + PINCTRL_FUNC_SWDT1_RST = (48U), + PINCTRL_FUNC_PMU0 = (49U), + PINCTRL_FUNC_PCIE0 = (50U), + PINCTRL_FUNC_CSU0 = (51U), + PINCTRL_FUNC_DPAUX0 = (52U), + PINCTRL_FUNC_PJTAG0 = (53U), + PINCTRL_FUNC_TRACE0 = (54U), + PINCTRL_FUNC_TRACE0_CLK = (55U), + PINCTRL_FUNC_TESTSCAN0 = (56U), + END_FUNCTION = (57U), +}; + +#define MAX_FUNCTION END_FUNCTION + +// pinctrl pin numbers +enum { + PINCTRL_PIN_0, + PINCTRL_PIN_1, + PINCTRL_PIN_2, + PINCTRL_PIN_3, + PINCTRL_PIN_4, + PINCTRL_PIN_5, + PINCTRL_PIN_6, + PINCTRL_PIN_7, + PINCTRL_PIN_8, + PINCTRL_PIN_9, + PINCTRL_PIN_10, + PINCTRL_PIN_11, + PINCTRL_PIN_12, + PINCTRL_PIN_13, + PINCTRL_PIN_14, + PINCTRL_PIN_15, + PINCTRL_PIN_16, + PINCTRL_PIN_17, + PINCTRL_PIN_18, + PINCTRL_PIN_19, + PINCTRL_PIN_20, + PINCTRL_PIN_21, + PINCTRL_PIN_22, + PINCTRL_PIN_23, + PINCTRL_PIN_24, + PINCTRL_PIN_25, + PINCTRL_PIN_26, + PINCTRL_PIN_27, + PINCTRL_PIN_28, + PINCTRL_PIN_29, + PINCTRL_PIN_30, + PINCTRL_PIN_31, + PINCTRL_PIN_32, + PINCTRL_PIN_33, + PINCTRL_PIN_34, + PINCTRL_PIN_35, + PINCTRL_PIN_36, + PINCTRL_PIN_37, + PINCTRL_PIN_38, + PINCTRL_PIN_39, + PINCTRL_PIN_40, + PINCTRL_PIN_41, + PINCTRL_PIN_42, + PINCTRL_PIN_43, + PINCTRL_PIN_44, + PINCTRL_PIN_45, + PINCTRL_PIN_46, + PINCTRL_PIN_47, + PINCTRL_PIN_48, + PINCTRL_PIN_49, + PINCTRL_PIN_50, + PINCTRL_PIN_51, + PINCTRL_PIN_52, + PINCTRL_PIN_53, + PINCTRL_PIN_54, + PINCTRL_PIN_55, + PINCTRL_PIN_56, + PINCTRL_PIN_57, + PINCTRL_PIN_58, + PINCTRL_PIN_59, + PINCTRL_PIN_60, + PINCTRL_PIN_61, + PINCTRL_PIN_62, + PINCTRL_PIN_63, + PINCTRL_PIN_64, + PINCTRL_PIN_65, + PINCTRL_PIN_66, + PINCTRL_PIN_67, + PINCTRL_PIN_68, + PINCTRL_PIN_69, + PINCTRL_PIN_70, + PINCTRL_PIN_71, + PINCTRL_PIN_72, + PINCTRL_PIN_73, + PINCTRL_PIN_74, + PINCTRL_PIN_75, + PINCTRL_PIN_76, + PINCTRL_PIN_77, + END_PINS = (78U), +}; + +#define MAX_PIN END_PINS + +// pinctrl group ids +enum { + PINCTRL_GRP_ETHERNET0_0, + PINCTRL_GRP_ETHERNET1_0, + PINCTRL_GRP_ETHERNET2_0, + PINCTRL_GRP_ETHERNET3_0, + PINCTRL_GRP_GEMTSU0_0, + PINCTRL_GRP_GEMTSU0_1, + PINCTRL_GRP_GEMTSU0_2, + PINCTRL_GRP_MDIO0_0, + PINCTRL_GRP_MDIO1_0, + PINCTRL_GRP_MDIO1_1, + PINCTRL_GRP_MDIO2_0, + PINCTRL_GRP_MDIO3_0, + PINCTRL_GRP_QSPI0_0, + PINCTRL_GRP_QSPI_SS, + PINCTRL_GRP_QSPI_FBCLK, + PINCTRL_GRP_SPI0_0, + PINCTRL_GRP_SPI0_1, + PINCTRL_GRP_SPI0_2, + PINCTRL_GRP_SPI0_3, + PINCTRL_GRP_SPI0_4, + PINCTRL_GRP_SPI0_5, + PINCTRL_GRP_SPI0_0_SS0, + PINCTRL_GRP_SPI0_0_SS1, + PINCTRL_GRP_SPI0_0_SS2, + PINCTRL_GRP_SPI0_1_SS0, + PINCTRL_GRP_SPI0_1_SS1, + PINCTRL_GRP_SPI0_1_SS2, + PINCTRL_GRP_SPI0_2_SS0, + PINCTRL_GRP_SPI0_2_SS1, + PINCTRL_GRP_SPI0_2_SS2, + PINCTRL_GRP_SPI0_3_SS0, + PINCTRL_GRP_SPI0_3_SS1, + PINCTRL_GRP_SPI0_3_SS2, + PINCTRL_GRP_SPI0_4_SS0, + PINCTRL_GRP_SPI0_4_SS1, + PINCTRL_GRP_SPI0_4_SS2, + PINCTRL_GRP_SPI0_5_SS0, + PINCTRL_GRP_SPI0_5_SS1, + PINCTRL_GRP_SPI0_5_SS2, + PINCTRL_GRP_SPI1_0, + PINCTRL_GRP_SPI1_1, + PINCTRL_GRP_SPI1_2, + PINCTRL_GRP_SPI1_3, + PINCTRL_GRP_SPI1_4, + PINCTRL_GRP_SPI1_5, + PINCTRL_GRP_SPI1_0_SS0, + PINCTRL_GRP_SPI1_0_SS1, + PINCTRL_GRP_SPI1_0_SS2, + PINCTRL_GRP_SPI1_1_SS0, + PINCTRL_GRP_SPI1_1_SS1, + PINCTRL_GRP_SPI1_1_SS2, + PINCTRL_GRP_SPI1_2_SS0, + PINCTRL_GRP_SPI1_2_SS1, + PINCTRL_GRP_SPI1_2_SS2, + PINCTRL_GRP_SPI1_3_SS0, + PINCTRL_GRP_SPI1_3_SS1, + PINCTRL_GRP_SPI1_3_SS2, + PINCTRL_GRP_SPI1_4_SS0, + PINCTRL_GRP_SPI1_4_SS1, + PINCTRL_GRP_SPI1_4_SS2, + PINCTRL_GRP_SPI1_5_SS0, + PINCTRL_GRP_SPI1_5_SS1, + PINCTRL_GRP_SPI1_5_SS2, + PINCTRL_GRP_SDIO0_0, + PINCTRL_GRP_SDIO0_1, + PINCTRL_GRP_SDIO0_2, + PINCTRL_GRP_SDIO0_4BIT_0_0, + PINCTRL_GRP_SDIO0_4BIT_0_1, + PINCTRL_GRP_SDIO0_4BIT_1_0, + PINCTRL_GRP_SDIO0_4BIT_1_1, + PINCTRL_GRP_SDIO0_4BIT_2_0, + PINCTRL_GRP_SDIO0_4BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_0_0, + PINCTRL_GRP_SDIO0_1BIT_0_1, + PINCTRL_GRP_SDIO0_1BIT_0_2, + PINCTRL_GRP_SDIO0_1BIT_0_3, + PINCTRL_GRP_SDIO0_1BIT_0_4, + PINCTRL_GRP_SDIO0_1BIT_0_5, + PINCTRL_GRP_SDIO0_1BIT_0_6, + PINCTRL_GRP_SDIO0_1BIT_0_7, + PINCTRL_GRP_SDIO0_1BIT_1_0, + PINCTRL_GRP_SDIO0_1BIT_1_1, + PINCTRL_GRP_SDIO0_1BIT_1_2, + PINCTRL_GRP_SDIO0_1BIT_1_3, + PINCTRL_GRP_SDIO0_1BIT_1_4, + PINCTRL_GRP_SDIO0_1BIT_1_5, + PINCTRL_GRP_SDIO0_1BIT_1_6, + PINCTRL_GRP_SDIO0_1BIT_1_7, + PINCTRL_GRP_SDIO0_1BIT_2_0, + PINCTRL_GRP_SDIO0_1BIT_2_1, + PINCTRL_GRP_SDIO0_1BIT_2_2, + PINCTRL_GRP_SDIO0_1BIT_2_3, + PINCTRL_GRP_SDIO0_1BIT_2_4, + PINCTRL_GRP_SDIO0_1BIT_2_5, + PINCTRL_GRP_SDIO0_1BIT_2_6, + PINCTRL_GRP_SDIO0_1BIT_2_7, + PINCTRL_GRP_SDIO0_0_PC, + PINCTRL_GRP_SDIO0_1_PC, + PINCTRL_GRP_SDIO0_2_PC, + PINCTRL_GRP_SDIO0_0_CD, + PINCTRL_GRP_SDIO0_1_CD, + PINCTRL_GRP_SDIO0_2_CD, + PINCTRL_GRP_SDIO0_0_WP, + PINCTRL_GRP_SDIO0_1_WP, + PINCTRL_GRP_SDIO0_2_WP, + PINCTRL_GRP_SDIO1_0, + PINCTRL_GRP_SDIO1_4BIT_0_0, + PINCTRL_GRP_SDIO1_4BIT_0_1, + PINCTRL_GRP_SDIO1_4BIT_1_0, + PINCTRL_GRP_SDIO1_1BIT_0_0, + PINCTRL_GRP_SDIO1_1BIT_0_1, + PINCTRL_GRP_SDIO1_1BIT_0_2, + PINCTRL_GRP_SDIO1_1BIT_0_3, + PINCTRL_GRP_SDIO1_1BIT_0_4, + PINCTRL_GRP_SDIO1_1BIT_0_5, + PINCTRL_GRP_SDIO1_1BIT_0_6, + PINCTRL_GRP_SDIO1_1BIT_0_7, + PINCTRL_GRP_SDIO1_1BIT_1_0, + PINCTRL_GRP_SDIO1_1BIT_1_1, + PINCTRL_GRP_SDIO1_1BIT_1_2, + PINCTRL_GRP_SDIO1_1BIT_1_3, + PINCTRL_GRP_SDIO1_0_PC, + PINCTRL_GRP_SDIO1_1_PC, + PINCTRL_GRP_SDIO1_0_CD, + PINCTRL_GRP_SDIO1_1_CD, + PINCTRL_GRP_SDIO1_0_WP, + PINCTRL_GRP_SDIO1_1_WP, + PINCTRL_GRP_NAND0_0, + PINCTRL_GRP_NAND0_0_CE, + PINCTRL_GRP_NAND0_1_CE, + PINCTRL_GRP_NAND0_0_RB, + PINCTRL_GRP_NAND0_1_RB, + PINCTRL_GRP_NAND0_0_DQS, + PINCTRL_GRP_NAND0_1_DQS, + PINCTRL_GRP_CAN0_0, + PINCTRL_GRP_CAN0_1, + PINCTRL_GRP_CAN0_2, + PINCTRL_GRP_CAN0_3, + PINCTRL_GRP_CAN0_4, + PINCTRL_GRP_CAN0_5, + PINCTRL_GRP_CAN0_6, + PINCTRL_GRP_CAN0_7, + PINCTRL_GRP_CAN0_8, + PINCTRL_GRP_CAN0_9, + PINCTRL_GRP_CAN0_10, + PINCTRL_GRP_CAN0_11, + PINCTRL_GRP_CAN0_12, + PINCTRL_GRP_CAN0_13, + PINCTRL_GRP_CAN0_14, + PINCTRL_GRP_CAN0_15, + PINCTRL_GRP_CAN0_16, + PINCTRL_GRP_CAN0_17, + PINCTRL_GRP_CAN0_18, + PINCTRL_GRP_CAN1_0, + PINCTRL_GRP_CAN1_1, + PINCTRL_GRP_CAN1_2, + PINCTRL_GRP_CAN1_3, + PINCTRL_GRP_CAN1_4, + PINCTRL_GRP_CAN1_5, + PINCTRL_GRP_CAN1_6, + PINCTRL_GRP_CAN1_7, + PINCTRL_GRP_CAN1_8, + PINCTRL_GRP_CAN1_9, + PINCTRL_GRP_CAN1_10, + PINCTRL_GRP_CAN1_11, + PINCTRL_GRP_CAN1_12, + PINCTRL_GRP_CAN1_13, + PINCTRL_GRP_CAN1_14, + PINCTRL_GRP_CAN1_15, + PINCTRL_GRP_CAN1_16, + PINCTRL_GRP_CAN1_17, + PINCTRL_GRP_CAN1_18, + PINCTRL_GRP_CAN1_19, + PINCTRL_GRP_UART0_0, + PINCTRL_GRP_UART0_1, + PINCTRL_GRP_UART0_2, + PINCTRL_GRP_UART0_3, + PINCTRL_GRP_UART0_4, + PINCTRL_GRP_UART0_5, + PINCTRL_GRP_UART0_6, + PINCTRL_GRP_UART0_7, + PINCTRL_GRP_UART0_8, + PINCTRL_GRP_UART0_9, + PINCTRL_GRP_UART0_10, + PINCTRL_GRP_UART0_11, + PINCTRL_GRP_UART0_12, + PINCTRL_GRP_UART0_13, + PINCTRL_GRP_UART0_14, + PINCTRL_GRP_UART0_15, + PINCTRL_GRP_UART0_16, + PINCTRL_GRP_UART0_17, + PINCTRL_GRP_UART0_18, + PINCTRL_GRP_UART1_0, + PINCTRL_GRP_UART1_1, + PINCTRL_GRP_UART1_2, + PINCTRL_GRP_UART1_3, + PINCTRL_GRP_UART1_4, + PINCTRL_GRP_UART1_5, + PINCTRL_GRP_UART1_6, + PINCTRL_GRP_UART1_7, + PINCTRL_GRP_UART1_8, + PINCTRL_GRP_UART1_9, + PINCTRL_GRP_UART1_10, + PINCTRL_GRP_UART1_11, + PINCTRL_GRP_UART1_12, + PINCTRL_GRP_UART1_13, + PINCTRL_GRP_UART1_14, + PINCTRL_GRP_UART1_15, + PINCTRL_GRP_UART1_16, + PINCTRL_GRP_UART1_17, + PINCTRL_GRP_UART1_18, + PINCTRL_GRP_I2C0_0, + PINCTRL_GRP_I2C0_1, + PINCTRL_GRP_I2C0_2, + PINCTRL_GRP_I2C0_3, + PINCTRL_GRP_I2C0_4, + PINCTRL_GRP_I2C0_5, + PINCTRL_GRP_I2C0_6, + PINCTRL_GRP_I2C0_7, + PINCTRL_GRP_I2C0_8, + PINCTRL_GRP_I2C0_9, + PINCTRL_GRP_I2C0_10, + PINCTRL_GRP_I2C0_11, + PINCTRL_GRP_I2C0_12, + PINCTRL_GRP_I2C0_13, + PINCTRL_GRP_I2C0_14, + PINCTRL_GRP_I2C0_15, + PINCTRL_GRP_I2C0_16, + PINCTRL_GRP_I2C0_17, + PINCTRL_GRP_I2C0_18, + PINCTRL_GRP_I2C1_0, + PINCTRL_GRP_I2C1_1, + PINCTRL_GRP_I2C1_2, + PINCTRL_GRP_I2C1_3, + PINCTRL_GRP_I2C1_4, + PINCTRL_GRP_I2C1_5, + PINCTRL_GRP_I2C1_6, + PINCTRL_GRP_I2C1_7, + PINCTRL_GRP_I2C1_8, + PINCTRL_GRP_I2C1_9, + PINCTRL_GRP_I2C1_10, + PINCTRL_GRP_I2C1_11, + PINCTRL_GRP_I2C1_12, + PINCTRL_GRP_I2C1_13, + PINCTRL_GRP_I2C1_14, + PINCTRL_GRP_I2C1_15, + PINCTRL_GRP_I2C1_16, + PINCTRL_GRP_I2C1_17, + PINCTRL_GRP_I2C1_18, + PINCTRL_GRP_I2C1_19, + PINCTRL_GRP_TTC0_0_CLK, + PINCTRL_GRP_TTC0_1_CLK, + PINCTRL_GRP_TTC0_2_CLK, + PINCTRL_GRP_TTC0_3_CLK, + PINCTRL_GRP_TTC0_4_CLK, + PINCTRL_GRP_TTC0_5_CLK, + PINCTRL_GRP_TTC0_6_CLK, + PINCTRL_GRP_TTC0_7_CLK, + PINCTRL_GRP_TTC0_8_CLK, + PINCTRL_GRP_TTC0_0_WAV, + PINCTRL_GRP_TTC0_1_WAV, + PINCTRL_GRP_TTC0_2_WAV, + PINCTRL_GRP_TTC0_3_WAV, + PINCTRL_GRP_TTC0_4_WAV, + PINCTRL_GRP_TTC0_5_WAV, + PINCTRL_GRP_TTC0_6_WAV, + PINCTRL_GRP_TTC0_7_WAV, + PINCTRL_GRP_TTC0_8_WAV, + PINCTRL_GRP_TTC1_0_CLK, + PINCTRL_GRP_TTC1_1_CLK, + PINCTRL_GRP_TTC1_2_CLK, + PINCTRL_GRP_TTC1_3_CLK, + PINCTRL_GRP_TTC1_4_CLK, + PINCTRL_GRP_TTC1_5_CLK, + PINCTRL_GRP_TTC1_6_CLK, + PINCTRL_GRP_TTC1_7_CLK, + PINCTRL_GRP_TTC1_8_CLK, + PINCTRL_GRP_TTC1_0_WAV, + PINCTRL_GRP_TTC1_1_WAV, + PINCTRL_GRP_TTC1_2_WAV, + PINCTRL_GRP_TTC1_3_WAV, + PINCTRL_GRP_TTC1_4_WAV, + PINCTRL_GRP_TTC1_5_WAV, + PINCTRL_GRP_TTC1_6_WAV, + PINCTRL_GRP_TTC1_7_WAV, + PINCTRL_GRP_TTC1_8_WAV, + PINCTRL_GRP_TTC2_0_CLK, + PINCTRL_GRP_TTC2_1_CLK, + PINCTRL_GRP_TTC2_2_CLK, + PINCTRL_GRP_TTC2_3_CLK, + PINCTRL_GRP_TTC2_4_CLK, + PINCTRL_GRP_TTC2_5_CLK, + PINCTRL_GRP_TTC2_6_CLK, + PINCTRL_GRP_TTC2_7_CLK, + PINCTRL_GRP_TTC2_8_CLK, + PINCTRL_GRP_TTC2_0_WAV, + PINCTRL_GRP_TTC2_1_WAV, + PINCTRL_GRP_TTC2_2_WAV, + PINCTRL_GRP_TTC2_3_WAV, + PINCTRL_GRP_TTC2_4_WAV, + PINCTRL_GRP_TTC2_5_WAV, + PINCTRL_GRP_TTC2_6_WAV, + PINCTRL_GRP_TTC2_7_WAV, + PINCTRL_GRP_TTC2_8_WAV, + PINCTRL_GRP_TTC3_0_CLK, + PINCTRL_GRP_TTC3_1_CLK, + PINCTRL_GRP_TTC3_2_CLK, + PINCTRL_GRP_TTC3_3_CLK, + PINCTRL_GRP_TTC3_4_CLK, + PINCTRL_GRP_TTC3_5_CLK, + PINCTRL_GRP_TTC3_6_CLK, + PINCTRL_GRP_TTC3_7_CLK, + PINCTRL_GRP_TTC3_8_CLK, + PINCTRL_GRP_TTC3_0_WAV, + PINCTRL_GRP_TTC3_1_WAV, + PINCTRL_GRP_TTC3_2_WAV, + PINCTRL_GRP_TTC3_3_WAV, + PINCTRL_GRP_TTC3_4_WAV, + PINCTRL_GRP_TTC3_5_WAV, + PINCTRL_GRP_TTC3_6_WAV, + PINCTRL_GRP_TTC3_7_WAV, + PINCTRL_GRP_TTC3_8_WAV, + PINCTRL_GRP_SWDT0_0_CLK, + PINCTRL_GRP_SWDT0_1_CLK, + PINCTRL_GRP_SWDT0_2_CLK, + PINCTRL_GRP_SWDT0_3_CLK, + PINCTRL_GRP_SWDT0_4_CLK, + PINCTRL_GRP_SWDT0_5_CLK, + PINCTRL_GRP_SWDT0_6_CLK, + PINCTRL_GRP_SWDT0_7_CLK, + PINCTRL_GRP_SWDT0_8_CLK, + PINCTRL_GRP_SWDT0_9_CLK, + PINCTRL_GRP_SWDT0_10_CLK, + PINCTRL_GRP_SWDT0_11_CLK, + PINCTRL_GRP_SWDT0_12_CLK, + PINCTRL_GRP_SWDT0_0_RST, + PINCTRL_GRP_SWDT0_1_RST, + PINCTRL_GRP_SWDT0_2_RST, + PINCTRL_GRP_SWDT0_3_RST, + PINCTRL_GRP_SWDT0_4_RST, + PINCTRL_GRP_SWDT0_5_RST, + PINCTRL_GRP_SWDT0_6_RST, + PINCTRL_GRP_SWDT0_7_RST, + PINCTRL_GRP_SWDT0_8_RST, + PINCTRL_GRP_SWDT0_9_RST, + PINCTRL_GRP_SWDT0_10_RST, + PINCTRL_GRP_SWDT0_11_RST, + PINCTRL_GRP_SWDT0_12_RST, + PINCTRL_GRP_SWDT1_0_CLK, + PINCTRL_GRP_SWDT1_1_CLK, + PINCTRL_GRP_SWDT1_2_CLK, + PINCTRL_GRP_SWDT1_3_CLK, + PINCTRL_GRP_SWDT1_4_CLK, + PINCTRL_GRP_SWDT1_5_CLK, + PINCTRL_GRP_SWDT1_6_CLK, + PINCTRL_GRP_SWDT1_7_CLK, + PINCTRL_GRP_SWDT1_8_CLK, + PINCTRL_GRP_SWDT1_9_CLK, + PINCTRL_GRP_SWDT1_10_CLK, + PINCTRL_GRP_SWDT1_11_CLK, + PINCTRL_GRP_SWDT1_12_CLK, + PINCTRL_GRP_SWDT1_0_RST, + PINCTRL_GRP_SWDT1_1_RST, + PINCTRL_GRP_SWDT1_2_RST, + PINCTRL_GRP_SWDT1_3_RST, + PINCTRL_GRP_SWDT1_4_RST, + PINCTRL_GRP_SWDT1_5_RST, + PINCTRL_GRP_SWDT1_6_RST, + PINCTRL_GRP_SWDT1_7_RST, + PINCTRL_GRP_SWDT1_8_RST, + PINCTRL_GRP_SWDT1_9_RST, + PINCTRL_GRP_SWDT1_10_RST, + PINCTRL_GRP_SWDT1_11_RST, + PINCTRL_GRP_SWDT1_12_RST, + PINCTRL_GRP_GPIO0_0, + PINCTRL_GRP_GPIO0_1, + PINCTRL_GRP_GPIO0_2, + PINCTRL_GRP_GPIO0_3, + PINCTRL_GRP_GPIO0_4, + PINCTRL_GRP_GPIO0_5, + PINCTRL_GRP_GPIO0_6, + PINCTRL_GRP_GPIO0_7, + PINCTRL_GRP_GPIO0_8, + PINCTRL_GRP_GPIO0_9, + PINCTRL_GRP_GPIO0_10, + PINCTRL_GRP_GPIO0_11, + PINCTRL_GRP_GPIO0_12, + PINCTRL_GRP_GPIO0_13, + PINCTRL_GRP_GPIO0_14, + PINCTRL_GRP_GPIO0_15, + PINCTRL_GRP_GPIO0_16, + PINCTRL_GRP_GPIO0_17, + PINCTRL_GRP_GPIO0_18, + PINCTRL_GRP_GPIO0_19, + PINCTRL_GRP_GPIO0_20, + PINCTRL_GRP_GPIO0_21, + PINCTRL_GRP_GPIO0_22, + PINCTRL_GRP_GPIO0_23, + PINCTRL_GRP_GPIO0_24, + PINCTRL_GRP_GPIO0_25, + PINCTRL_GRP_GPIO0_26, + PINCTRL_GRP_GPIO0_27, + PINCTRL_GRP_GPIO0_28, + PINCTRL_GRP_GPIO0_29, + PINCTRL_GRP_GPIO0_30, + PINCTRL_GRP_GPIO0_31, + PINCTRL_GRP_GPIO0_32, + PINCTRL_GRP_GPIO0_33, + PINCTRL_GRP_GPIO0_34, + PINCTRL_GRP_GPIO0_35, + PINCTRL_GRP_GPIO0_36, + PINCTRL_GRP_GPIO0_37, + PINCTRL_GRP_GPIO0_38, + PINCTRL_GRP_GPIO0_39, + PINCTRL_GRP_GPIO0_40, + PINCTRL_GRP_GPIO0_41, + PINCTRL_GRP_GPIO0_42, + PINCTRL_GRP_GPIO0_43, + PINCTRL_GRP_GPIO0_44, + PINCTRL_GRP_GPIO0_45, + PINCTRL_GRP_GPIO0_46, + PINCTRL_GRP_GPIO0_47, + PINCTRL_GRP_GPIO0_48, + PINCTRL_GRP_GPIO0_49, + PINCTRL_GRP_GPIO0_50, + PINCTRL_GRP_GPIO0_51, + PINCTRL_GRP_GPIO0_52, + PINCTRL_GRP_GPIO0_53, + PINCTRL_GRP_GPIO0_54, + PINCTRL_GRP_GPIO0_55, + PINCTRL_GRP_GPIO0_56, + PINCTRL_GRP_GPIO0_57, + PINCTRL_GRP_GPIO0_58, + PINCTRL_GRP_GPIO0_59, + PINCTRL_GRP_GPIO0_60, + PINCTRL_GRP_GPIO0_61, + PINCTRL_GRP_GPIO0_62, + PINCTRL_GRP_GPIO0_63, + PINCTRL_GRP_GPIO0_64, + PINCTRL_GRP_GPIO0_65, + PINCTRL_GRP_GPIO0_66, + PINCTRL_GRP_GPIO0_67, + PINCTRL_GRP_GPIO0_68, + PINCTRL_GRP_GPIO0_69, + PINCTRL_GRP_GPIO0_70, + PINCTRL_GRP_GPIO0_71, + PINCTRL_GRP_GPIO0_72, + PINCTRL_GRP_GPIO0_73, + PINCTRL_GRP_GPIO0_74, + PINCTRL_GRP_GPIO0_75, + PINCTRL_GRP_GPIO0_76, + PINCTRL_GRP_GPIO0_77, + PINCTRL_GRP_USB0_0, + PINCTRL_GRP_USB1_0, + PINCTRL_GRP_PMU0_0, + PINCTRL_GRP_PMU0_1, + PINCTRL_GRP_PMU0_2, + PINCTRL_GRP_PMU0_3, + PINCTRL_GRP_PMU0_4, + PINCTRL_GRP_PMU0_5, + PINCTRL_GRP_PMU0_6, + PINCTRL_GRP_PMU0_7, + PINCTRL_GRP_PMU0_8, + PINCTRL_GRP_PMU0_9, + PINCTRL_GRP_PMU0_10, + PINCTRL_GRP_PMU0_11, + PINCTRL_GRP_PCIE0_0, + PINCTRL_GRP_PCIE0_1, + PINCTRL_GRP_PCIE0_2, + PINCTRL_GRP_PCIE0_3, + PINCTRL_GRP_PCIE0_4, + PINCTRL_GRP_PCIE0_5, + PINCTRL_GRP_PCIE0_6, + PINCTRL_GRP_PCIE0_7, + PINCTRL_GRP_CSU0_0, + PINCTRL_GRP_CSU0_1, + PINCTRL_GRP_CSU0_2, + PINCTRL_GRP_CSU0_3, + PINCTRL_GRP_CSU0_4, + PINCTRL_GRP_CSU0_5, + PINCTRL_GRP_CSU0_6, + PINCTRL_GRP_CSU0_7, + PINCTRL_GRP_CSU0_8, + PINCTRL_GRP_CSU0_9, + PINCTRL_GRP_CSU0_10, + PINCTRL_GRP_CSU0_11, + PINCTRL_GRP_DPAUX0_0, + PINCTRL_GRP_DPAUX0_1, + PINCTRL_GRP_DPAUX0_2, + PINCTRL_GRP_DPAUX0_3, + PINCTRL_GRP_PJTAG0_0, + PINCTRL_GRP_PJTAG0_1, + PINCTRL_GRP_PJTAG0_2, + PINCTRL_GRP_PJTAG0_3, + PINCTRL_GRP_PJTAG0_4, + PINCTRL_GRP_PJTAG0_5, + PINCTRL_GRP_TRACE0_0, + PINCTRL_GRP_TRACE0_1, + PINCTRL_GRP_TRACE0_2, + PINCTRL_GRP_TRACE0_0_CLK, + PINCTRL_GRP_TRACE0_1_CLK, + PINCTRL_GRP_TRACE0_2_CLK, + PINCTRL_GRP_TESTSCAN0_0, +}; + +// pinctrl config parameters +enum { + PINCTRL_CONFIG_SLEW_RATE, + PINCTRL_CONFIG_BIAS_STATUS, + PINCTRL_CONFIG_PULL_CTRL, + PINCTRL_CONFIG_SCHMITT_CMOS, + PINCTRL_CONFIG_DRIVE_STRENGTH, + PINCTRL_CONFIG_VOLTAGE_STATUS, + PINCTRL_CONFIG_MAX, +}; + +// pinctrl slew rate +#define PINCTRL_SLEW_RATE_FAST 0U +#define PINCTRL_SLEW_RATE_SLOW 1U + +// pinctrl bias status +#define PINCTRL_BIAS_DISABLE 0U +#define PINCTRL_BIAS_ENABLE 1U + +// pinctrl pull control +#define PINCTRL_BIAS_PULL_DOWN 0U +#define PINCTRL_BIAS_PULL_UP 1U + +// pinctrl schmitt cmos type +#define PINCTRL_INPUT_TYPE_CMOS 0U +#define PINCTRL_INPUT_TYPE_SCHMITT 1U + +//pinctrl drive strength values +#define PINCTRL_DRIVE_STRENGTH_2MA 0U +#define PINCTRL_DRIVE_STRENGTH_4MA 1U +#define PINCTRL_DRIVE_STRENGTH_8MA 2U +#define PINCTRL_DRIVE_STRENGTH_12MA 3U + +void pm_api_pinctrl_get_function_name(uint32_t fid, char *name); +enum pm_ret_status pm_api_pinctrl_get_function_groups(uint32_t fid, + uint32_t index, + uint16_t *groups); +enum pm_ret_status pm_api_pinctrl_get_pin_groups(uint32_t pin, + uint32_t index, + uint16_t *groups); +enum pm_ret_status pm_api_pinctrl_get_num_pins(uint32_t *npins); +enum pm_ret_status pm_api_pinctrl_get_num_functions(uint32_t *nfuncs); +enum pm_ret_status pm_api_pinctrl_get_num_func_groups(uint32_t fid, + uint32_t *ngroups); +#endif /* PM_API_PINCTRL_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.c b/plat/xilinx/zynqmp/pm_service/pm_client.c new file mode 100644 index 0000000..4afa01d --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/pm_client.c @@ -0,0 +1,364 @@ +/* + * 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 + */ + +/* + * APU specific definition of processors in the subsystem as well as functions + * for getting information about and changing state of the APU. + */ + +#include <assert.h> +#include <string.h> + +#include <common/bl_common.h> +#include <drivers/arm/gic_common.h> +#include <drivers/arm/gicv2.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <lib/utils.h> + +#include <plat_ipi.h> +#include "pm_client.h" +#include "pm_ipi.h" +#include <zynqmp_def.h> +#include "zynqmp_pm_api_sys.h" + +#define IRQ_MAX 84U +#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5U) + 1U) +#define UNDEFINED_CPUID (~0U) + +#define PM_SUSPEND_MODE_STD 0U +#define PM_SUSPEND_MODE_POWER_OFF 1U + +DEFINE_BAKERY_LOCK(pm_client_secure_lock); + +static const struct pm_ipi apu_ipi = { + .local_ipi_id = IPI_LOCAL_ID, + .remote_ipi_id = IPI_REMOTE_ID, + .buffer_base = IPI_BUFFER_LOCAL_BASE, +}; + +static uint32_t suspend_mode = PM_SUSPEND_MODE_STD; + +/* Order in pm_procs_all array must match cpu ids */ +static const struct pm_proc pm_procs_all[] = { + { + .node_id = NODE_APU_0, + .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK, + .ipi = &apu_ipi, + }, + { + .node_id = NODE_APU_1, + .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK, + .ipi = &apu_ipi, + }, + { + .node_id = NODE_APU_2, + .pwrdn_mask = APU_2_PWRCTL_CPUPWRDWNREQ_MASK, + .ipi = &apu_ipi, + }, + { + .node_id = NODE_APU_3, + .pwrdn_mask = APU_3_PWRCTL_CPUPWRDWNREQ_MASK, + .ipi = &apu_ipi, + }, +}; + +/* Interrupt to PM node ID map */ +static enum pm_node_id irq_node_map[IRQ_MAX + 1U] = { + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 3 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 7 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 11 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_NAND, + NODE_QSPI, /* 15 */ + NODE_GPIO, + NODE_I2C_0, + NODE_I2C_1, + NODE_SPI_0, /* 19 */ + NODE_SPI_1, + NODE_UART_0, + NODE_UART_1, + NODE_CAN_0, /* 23 */ + NODE_CAN_1, + NODE_UNKNOWN, + NODE_RTC, + NODE_RTC, /* 27 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 31 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 35, NODE_IPI_APU */ + NODE_TTC_0, + NODE_TTC_0, + NODE_TTC_0, + NODE_TTC_1, /* 39 */ + NODE_TTC_1, + NODE_TTC_1, + NODE_TTC_2, + NODE_TTC_2, /* 43 */ + NODE_TTC_2, + NODE_TTC_3, + NODE_TTC_3, + NODE_TTC_3, /* 47 */ + NODE_SD_0, + NODE_SD_1, + NODE_SD_0, + NODE_SD_1, /* 51 */ + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, + NODE_UNKNOWN, /* 55 */ + NODE_UNKNOWN, + NODE_ETH_0, + NODE_ETH_0, + NODE_ETH_1, /* 59 */ + NODE_ETH_1, + NODE_ETH_2, + NODE_ETH_2, + NODE_ETH_3, /* 63 */ + NODE_ETH_3, + NODE_USB_0, + NODE_USB_0, + NODE_USB_0, /* 67 */ + NODE_USB_0, + NODE_USB_0, + NODE_USB_1, + NODE_USB_1, /* 71 */ + NODE_USB_1, + NODE_USB_1, + NODE_USB_1, + NODE_USB_0, /* 75 */ + NODE_USB_0, + NODE_ADMA, + NODE_ADMA, + NODE_ADMA, /* 79 */ + NODE_ADMA, + NODE_ADMA, + NODE_ADMA, + NODE_ADMA, /* 83 */ + NODE_ADMA, +}; + +/** + * irq_to_pm_node - Get PM node ID corresponding to the interrupt number. + * @irq: Interrupt number. + * + * Return: PM node ID corresponding to the specified interrupt. + * + */ +static enum pm_node_id irq_to_pm_node(uint32_t irq) +{ + assert(irq <= IRQ_MAX); + return irq_node_map[irq]; +} + +/** + * pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake + * sources in the PMU firmware. + * + */ +static void pm_client_set_wakeup_sources(void) +{ + uint32_t reg_num; + uint8_t pm_wakeup_nodes_set[NODE_MAX] = { 0 }; + uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4U; + + /* In case of power-off suspend, only NODE_EXTERN must be set */ + if (suspend_mode == PM_SUSPEND_MODE_POWER_OFF) { + enum pm_ret_status ret; + + ret = pm_set_wakeup_source(NODE_APU, NODE_EXTERN, 1U); + /** + * If NODE_EXTERN could not be set as wake source, proceed with + * standard suspend (no one will wake the system otherwise) + */ + if (ret == PM_RET_SUCCESS) { + return; + } + } + + zeromem(&pm_wakeup_nodes_set, 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 << 2U)); + + if (reg == 0) { + continue; + } + + while (reg) { + enum pm_node_id node; + uint32_t idx, ret, irq, lowest_set = reg & (-reg); + + idx = __builtin_ctz(lowest_set); + irq = base_irq + idx; + + if (irq > IRQ_MAX) { + break; + } + + node = irq_to_pm_node(irq); + reg &= ~lowest_set; + + if (node > NODE_UNKNOWN && node < NODE_MAX) { + if (pm_wakeup_nodes_set[node] == 0U) { + ret = pm_set_wakeup_source(NODE_APU, node, 1U); + pm_wakeup_nodes_set[node] = (ret == PM_RET_SUCCESS) ? 1U : 0U; + } + } + } + } +} + +/** + * pm_get_proc() - returns pointer to the proc structure. + * @cpuid: id of the cpu whose proc struct pointer should be returned. + * + * Return: pointer to a proc structure if proc is found, otherwise NULL. + * + */ +const struct pm_proc *pm_get_proc(uint32_t cpuid) +{ + if (cpuid < ARRAY_SIZE(pm_procs_all)) { + return &pm_procs_all[cpuid]; + } + + return NULL; +} + +/** + * pm_get_proc_by_node() - returns pointer to the proc structure. + * @nid: node id of the processor. + * + * Return: pointer to a proc structure if proc is found, otherwise NULL. + * + */ +const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid) +{ + for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { + if (nid == pm_procs_all[i].node_id) { + return &pm_procs_all[i]; + } + } + return NULL; +} + +/** + * pm_get_cpuid() - get the local cpu ID for a global node ID. + * @nid: node id of the processor. + * + * Return: the cpu ID (starting from 0) for the subsystem. + * + */ +static uint32_t pm_get_cpuid(enum pm_node_id nid) +{ + for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { + if (pm_procs_all[i].node_id == nid) { + return i; + } + } + return UNDEFINED_CPUID; +} + +const struct pm_proc *primary_proc = &pm_procs_all[0]; + +/** + * pm_client_suspend() - Client-specific suspend actions. + * @proc: processor which need to suspend. + * @state: desired suspend state. + * + * This function should contain any PU-specific actions + * required prior to sending suspend request to PMU + * Actions taken depend on the state system is suspending to. + * + */ +void pm_client_suspend(const struct pm_proc *proc, uint32_t state) +{ + bakery_lock_get(&pm_client_secure_lock); + + if (state == PM_STATE_SUSPEND_TO_RAM) { + pm_client_set_wakeup_sources(); + } + + /* Set powerdown request */ + mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask); + + bakery_lock_release(&pm_client_secure_lock); +} + + +/** + * pm_client_abort_suspend() - Client-specific abort-suspend actions. + * + * This function should contain any PU-specific actions + * required for aborting a prior suspend request. + * + */ +void pm_client_abort_suspend(void) +{ + /* Enable interrupts at processor level (for current cpu) */ + gicv2_cpuif_enable(); + + bakery_lock_get(&pm_client_secure_lock); + + /* Clear powerdown request */ + mmio_write_32(APU_PWRCTL, + mmio_read_32(APU_PWRCTL) & ~primary_proc->pwrdn_mask); + + bakery_lock_release(&pm_client_secure_lock); +} + +/** + * pm_client_wakeup() - Client-specific wakeup actions. + * @proc: Processor which need to wakeup. + * + * This function should contain any PU-specific actions + * required for waking up another APU core. + * + */ +void pm_client_wakeup(const struct pm_proc *proc) +{ + uint32_t cpuid = pm_get_cpuid(proc->node_id); + + if (cpuid == UNDEFINED_CPUID) { + return; + } + + bakery_lock_get(&pm_client_secure_lock); + + /* clear powerdown bit for affected cpu */ + uint32_t val = mmio_read_32(APU_PWRCTL); + val &= ~(proc->pwrdn_mask); + mmio_write_32(APU_PWRCTL, val); + + bakery_lock_release(&pm_client_secure_lock); +} + +enum pm_ret_status pm_set_suspend_mode(uint32_t mode) +{ + if ((mode != PM_SUSPEND_MODE_STD) && + (mode != PM_SUSPEND_MODE_POWER_OFF)) { + return PM_RET_ERROR_ARGS; + } + + suspend_mode = mode; + return PM_RET_SUCCESS; +} diff --git a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.c new file mode 100644 index 0000000..6b42055 --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.c @@ -0,0 +1,1818 @@ +/* + * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * ZynqMP system level PM-API functions and communication with PMU via + * IPI interrupts + */ + +#include <arch_helpers.h> +#include <plat/common/platform.h> + +#include "pm_api_clock.h" +#include "pm_api_ioctl.h" +#include "pm_api_pinctrl.h" +#include "pm_client.h" +#include "pm_common.h" +#include "pm_ipi.h" +#include "zynqmp_pm_api_sys.h" + +#define PM_QUERY_FEATURE_BITMASK ( \ + (1ULL << (uint64_t)PM_QID_CLOCK_GET_NAME) | \ + (1ULL << (uint64_t)PM_QID_CLOCK_GET_TOPOLOGY) | \ + (1ULL << (uint64_t)PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS) | \ + (1ULL << (uint64_t)PM_QID_CLOCK_GET_PARENTS) | \ + (1ULL << (uint64_t)PM_QID_CLOCK_GET_ATTRIBUTES) | \ + (1ULL << (uint64_t)PM_QID_PINCTRL_GET_NUM_PINS) | \ + (1ULL << (uint64_t)PM_QID_PINCTRL_GET_NUM_FUNCTIONS) | \ + (1ULL << (uint64_t)PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS) | \ + (1ULL << (uint64_t)PM_QID_PINCTRL_GET_FUNCTION_NAME) | \ + (1ULL << (uint64_t)PM_QID_PINCTRL_GET_FUNCTION_GROUPS) | \ + (1ULL << (uint64_t)PM_QID_PINCTRL_GET_PIN_GROUPS) | \ + (1ULL << (uint64_t)PM_QID_CLOCK_GET_NUM_CLOCKS) | \ + (1ULL << (uint64_t)PM_QID_CLOCK_GET_MAX_DIVISOR)) + +/** + * typedef eemi_api_dependency - Dependent EEMI APIs which are implemented + * on both the TF-A and firmware. + * @id: EEMI API id or IOCTL id to be checked. + * @api_id: Dependent EEMI API. + * + */ +typedef struct __attribute__((packed)) { + uint8_t id; + uint8_t api_id; +} eemi_api_dependency; + +/* Dependent APIs for TF-A to check their version from firmware */ +static const eemi_api_dependency api_dep_table[] = { + { + .id = PM_SELF_SUSPEND, + .api_id = PM_SELF_SUSPEND, + }, + { + .id = PM_REQ_WAKEUP, + .api_id = PM_REQ_WAKEUP, + }, + { + .id = PM_ABORT_SUSPEND, + .api_id = PM_ABORT_SUSPEND, + }, + { + .id = PM_SET_WAKEUP_SOURCE, + .api_id = PM_SET_WAKEUP_SOURCE, + }, + { + .id = PM_SYSTEM_SHUTDOWN, + .api_id = PM_SYSTEM_SHUTDOWN, + }, + { + .id = PM_GET_API_VERSION, + .api_id = PM_GET_API_VERSION, + }, + { + .id = PM_CLOCK_ENABLE, + .api_id = PM_PLL_SET_MODE, + }, + { + .id = PM_CLOCK_ENABLE, + .api_id = PM_CLOCK_ENABLE, + }, + { + .id = PM_CLOCK_DISABLE, + .api_id = PM_PLL_SET_MODE, + }, + { + .id = PM_CLOCK_DISABLE, + .api_id = PM_CLOCK_DISABLE, + }, + { + .id = PM_CLOCK_GETSTATE, + .api_id = PM_PLL_GET_MODE, + }, + { + .id = PM_CLOCK_GETSTATE, + .api_id = PM_CLOCK_GETSTATE, + }, + { + .id = PM_CLOCK_SETDIVIDER, + .api_id = PM_PLL_SET_PARAMETER, + }, + { + .id = PM_CLOCK_SETDIVIDER, + .api_id = PM_CLOCK_SETDIVIDER, + }, + { + .id = PM_CLOCK_GETDIVIDER, + .api_id = PM_PLL_GET_PARAMETER, + }, + { + .id = PM_CLOCK_GETDIVIDER, + .api_id = PM_CLOCK_GETDIVIDER, + }, + { + .id = PM_CLOCK_SETPARENT, + .api_id = PM_PLL_SET_PARAMETER, + }, + { + .id = PM_CLOCK_SETPARENT, + .api_id = PM_CLOCK_SETPARENT, + }, + { + .id = PM_CLOCK_GETPARENT, + .api_id = PM_PLL_GET_PARAMETER, + }, + { + .id = PM_CLOCK_GETPARENT, + .api_id = PM_CLOCK_GETPARENT, + }, + { + .id = PM_PLL_SET_PARAMETER, + .api_id = PM_PLL_SET_PARAMETER, + }, + { + .id = PM_PLL_GET_PARAMETER, + .api_id = PM_PLL_GET_PARAMETER, + }, + { + .id = PM_PLL_SET_MODE, + .api_id = PM_PLL_SET_MODE, + }, + { + .id = PM_PLL_GET_MODE, + .api_id = PM_PLL_GET_MODE, + }, + { + .id = PM_REGISTER_ACCESS, + .api_id = PM_MMIO_WRITE, + }, + { + .id = PM_REGISTER_ACCESS, + .api_id = PM_MMIO_READ, + }, + { + .id = PM_FEATURE_CHECK, + .api_id = PM_FEATURE_CHECK, + }, + { + .id = IOCTL_SET_TAPDELAY_BYPASS, + .api_id = PM_MMIO_WRITE, + }, + { + .id = IOCTL_SD_DLL_RESET, + .api_id = PM_MMIO_WRITE, + }, + { + .id = IOCTL_SET_SD_TAPDELAY, + .api_id = PM_MMIO_WRITE, + }, + { + .id = IOCTL_SET_SD_TAPDELAY, + .api_id = PM_MMIO_READ, + }, + { + .id = IOCTL_SET_PLL_FRAC_DATA, + .api_id = PM_PLL_SET_PARAMETER, + }, + { + .id = IOCTL_GET_PLL_FRAC_DATA, + .api_id = PM_PLL_GET_PARAMETER, + }, + { + .id = IOCTL_WRITE_GGS, + .api_id = PM_MMIO_WRITE, + }, + { + .id = IOCTL_READ_GGS, + .api_id = PM_MMIO_READ, + }, + { + .id = IOCTL_WRITE_PGGS, + .api_id = PM_MMIO_WRITE, + }, + { + .id = IOCTL_READ_PGGS, + .api_id = PM_MMIO_READ, + }, + { + .id = IOCTL_ULPI_RESET, + .api_id = PM_MMIO_WRITE, + }, + { + .id = IOCTL_SET_BOOT_HEALTH_STATUS, + .api_id = PM_MMIO_WRITE, + }, + { + .id = IOCTL_AFI, + .api_id = PM_MMIO_WRITE, + }, +}; + +/* Expected firmware API version to TF-A */ +static const uint8_t tfa_expected_ver_id[] = { + [PM_SELF_SUSPEND] = FW_API_BASE_VERSION, + [PM_REQ_WAKEUP] = FW_API_BASE_VERSION, + [PM_ABORT_SUSPEND] = FW_API_BASE_VERSION, + [PM_SET_WAKEUP_SOURCE] = FW_API_BASE_VERSION, + [PM_SYSTEM_SHUTDOWN] = FW_API_BASE_VERSION, + [PM_GET_API_VERSION] = FW_API_BASE_VERSION, + [PM_PLL_SET_MODE] = FW_API_BASE_VERSION, + [PM_PLL_GET_MODE] = FW_API_BASE_VERSION, + [PM_CLOCK_ENABLE] = FW_API_BASE_VERSION, + [PM_CLOCK_DISABLE] = FW_API_BASE_VERSION, + [PM_CLOCK_GETSTATE] = FW_API_BASE_VERSION, + [PM_PLL_SET_PARAMETER] = FW_API_BASE_VERSION, + [PM_PLL_GET_PARAMETER] = FW_API_BASE_VERSION, + [PM_CLOCK_SETDIVIDER] = FW_API_BASE_VERSION, + [PM_CLOCK_GETDIVIDER] = FW_API_BASE_VERSION, + [PM_CLOCK_SETPARENT] = FW_API_BASE_VERSION, + [PM_CLOCK_GETPARENT] = FW_API_BASE_VERSION, + [PM_MMIO_WRITE] = FW_API_BASE_VERSION, + [PM_MMIO_READ] = FW_API_BASE_VERSION, + [PM_FEATURE_CHECK] = FW_API_VERSION_2, +}; + +/* default shutdown/reboot scope is system(2) */ +static uint32_t pm_shutdown_scope = PMF_SHUTDOWN_SUBTYPE_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_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. + * + * 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(enum pm_node_id nid, + uint32_t latency, + uint32_t state, + uintptr_t address) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + uint32_t cpuid = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpuid); + + /* + * Do client specific suspend operations + * (e.g. set powerdown request bit) + */ + pm_client_suspend(proc, state); + /* Send request to the PMU */ + PM_PACK_PAYLOAD6(payload, PM_SELF_SUSPEND, proc->node_id, latency, + state, address, (address >> 32)); + return pm_ipi_send_sync(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). + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_req_suspend(enum pm_node_id target, + enum pm_request_ack ack, + uint32_t latency, uint32_t state) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_REQ_SUSPEND, target, ack, latency, state); + if (ack == REQ_ACK_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: Node id of the processor or subsystem to wake up. + * @ack: Flag to specify whether acknowledge requested. + * @set_address: Resume address presence indicator. + * 1 resume address specified, 0 otherwise. + * @address: Resume address. + * + * 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 PMU. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_req_wakeup(enum pm_node_id target, + uint32_t set_address, + uintptr_t address, + enum pm_request_ack ack) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + uint64_t encoded_address; + + + /* encode set Address into 1st bit of address */ + encoded_address = address; + encoded_address |= !!set_address; + + /* Send request to the PMU to perform the wake of the PU */ + PM_PACK_PAYLOAD5(payload, PM_REQ_WAKEUP, target, encoded_address, + encoded_address >> 32, ack); + + if (ack == REQ_ACK_BLOCKING) { + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + } else { + return pm_ipi_send(primary_proc, payload); + } +} + +/** + * pm_force_powerdown() - PM call to request for another PU or subsystem to + * be powered down forcefully. + * @target: Node id of the targeted PU or subsystem. + * @ack: Flag to specify whether acknowledge is requested. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_force_powerdown(enum pm_node_id target, + enum pm_request_ack ack) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_FORCE_POWERDOWN, target, ack); + + if (ack == REQ_ACK_BLOCKING) { + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + } else { + return pm_ipi_send(primary_proc, payload); + } +} + +/** + * pm_abort_suspend() - PM call to announce that a prior suspend request + * is to be aborted. + * @reason: Reason for the abort. + * + * 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 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 PMU */ + /* TODO: allow passing the node ID of the affected CPU */ + PM_PACK_PAYLOAD3(payload, PM_ABORT_SUSPEND, reason, + primary_proc->node_id); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_set_wakeup_source() - PM call to specify the wakeup source while + * suspended. + * @target: Node id of the targeted PU or subsystem. + * @wkup_node: Node id of the wakeup peripheral. + * @enable: Enable or disable the specified peripheral as wake source. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_set_wakeup_source(enum pm_node_id target, + enum pm_node_id wkup_node, + uint32_t enable) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD4(payload, PM_SET_WAKEUP_SOURCE, target, wkup_node, + enable); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * 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. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + if (type == PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY) { + /* Setting scope for subsequent PSCI reboot or shutdown */ + pm_shutdown_scope = subtype; + return PM_RET_SUCCESS; + } + + PM_PACK_PAYLOAD3(payload, PM_SYSTEM_SHUTDOWN, type, subtype); + return pm_ipi_send_non_blocking(primary_proc, payload); +} + +/* APIs for managing PM slaves: */ + +/** + * pm_req_node() - PM call to request a node with specific capabilities. + * @nid: Node id of the slave. + * @capabilities: Requested capabilities of the slave. + * @qos: Quality of service (not supported). + * @ack: Flag to specify whether acknowledge is requested. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_req_node(enum pm_node_id nid, + uint32_t capabilities, + uint32_t qos, + enum pm_request_ack ack) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD5(payload, PM_REQ_NODE, nid, capabilities, qos, ack); + + if (ack == REQ_ACK_BLOCKING) { + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + } else { + return pm_ipi_send(primary_proc, payload); + } +} + +/** + * pm_set_requirement() - PM call to set requirement for PM slaves. + * @nid: Node id of the slave. + * @capabilities: Requested capabilities of the slave. + * @qos: Quality of service (not supported). + * @ack: Flag to specify whether acknowledge is requested. + * + * This API function is to be used for slaves a PU already has requested. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_set_requirement(enum pm_node_id nid, + uint32_t capabilities, + uint32_t qos, + enum pm_request_ack ack) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD5(payload, PM_SET_REQUIREMENT, nid, capabilities, qos, + ack); + + if (ack == REQ_ACK_BLOCKING) { + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + } else { + return pm_ipi_send(primary_proc, payload); + } +} + +/* Miscellaneous API functions */ + +/** + * pm_get_api_version() - Get version number of PMU PM firmware. + * @version: Returns 32-bit version number of PMU Power Management Firmware. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_get_api_version(uint32_t *version) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD1(payload, PM_GET_API_VERSION); + return pm_ipi_send_sync(primary_proc, payload, version, 1); +} + +/** + * pm_get_node_status() - PM call to request a node's current status. + * @nid: Node id. + * @ret_buff: Buffer for the return values + * [0] - Current power state of the node + * [1] - Current requirements for the node (slave nodes only) + * [2] - Current usage status for the node (slave nodes only) + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_get_node_status(enum pm_node_id nid, + uint32_t *ret_buff) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD2(payload, PM_GET_NODE_STATUS, nid); + return pm_ipi_send_sync(primary_proc, payload, ret_buff, 3); +} + +/** + * pm_mmio_write() - Perform write to protected mmio. + * @address: Address to write to. + * @mask: Mask to apply. + * @value: Value to write. + * + * This function provides access to PM-related control registers + * that may not be directly accessible by a particular PU. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_mmio_write(uintptr_t address, + uint32_t mask, + uint32_t value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, PM_MMIO_WRITE, address, mask, value); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_mmio_read() - Read value from protected mmio. + * @address: Address to write to. + * @value: Value to write. + * + * This function provides access to PM-related control registers + * that may not be directly accessible by a particular PU. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_mmio_read(uintptr_t address, uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, PM_MMIO_READ, address); + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/** + * pm_fpga_load() - Load the bitstream into the PL. This function provides + * access to the xilfpga library to load the Bit-stream + * into PL. + * @address_low: lower 32-bit Linear memory space address. + * @address_high: higher 32-bit Linear memory space address. + * @size: Number of 32bit words. + * @flags: Additional flags or settings for the fpga operation. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_fpga_load(uint32_t address_low, + uint32_t address_high, + uint32_t size, + uint32_t flags) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_FPGA_LOAD, address_high, address_low, + size, flags); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_fpga_get_status() - Read value from fpga status register. + * @value: Value to read. + * + * This function provides access to the xilfpga library to get + * the fpga status. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_fpga_get_status(uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD1(payload, PM_FPGA_GET_STATUS); + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/** + * pm_get_chipid() - Read silicon ID registers. + * @value: Buffer for return values. Must be large enough to hold 8 bytes. + * + * Return: Returns silicon ID registers. + * + */ +enum pm_ret_status pm_get_chipid(uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD1(payload, PM_GET_CHIPID); + return pm_ipi_send_sync(primary_proc, payload, value, 2); +} + +/** + * pm_secure_rsaaes() - Load the secure images. + * @address_low: lower 32-bit Linear memory space address. + * @address_high: higher 32-bit Linear memory space address. + * @size: Number of 32bit words. + * @flags: Additional flags or settings for the fpga operation. + * + * This function provides access to the xilsecure library to load the + * authenticated, encrypted, and authenticated/encrypted images. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_secure_rsaaes(uint32_t address_low, + uint32_t address_high, + uint32_t size, + uint32_t flags) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_RSA_AES, address_high, address_low, + size, flags); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_aes_engine() - Aes data blob encryption/decryption. + * @address_low: lower 32-bit address of the AesParams structure. + * @address_high: higher 32-bit address of the AesParams structure. + * @value: Returned output value. + * + * This function provides access to the xilsecure library to + * encrypt/decrypt data blobs. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_aes_engine(uint32_t address_high, + uint32_t address_low, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_SECURE_AES, address_high, address_low); + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/** + * pm_get_callbackdata() - Read from IPI response buffer. + * @data: array of PAYLOAD_ARG_CNT elements. + * @count: Number of values to return. + * + * 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) +{ + enum pm_ret_status ret = PM_RET_SUCCESS; + /* Return if interrupt is not from PMU */ + if (!pm_ipi_irq_status(primary_proc)) { + return ret; + } + + ret = pm_ipi_buff_read_callb(data, count); + pm_ipi_irq_clear(primary_proc); + return ret; +} + +/** + * pm_ioctl() - PM IOCTL API for device control and configs. + * @nid: Node ID of the device. + * @ioctl_id: ID of the requested IOCTL. + * @arg1: Argument 1 to requested IOCTL call. + * @arg2: Argument 2 to requested IOCTL call. + * @value: Returned output value. + * + * This function calls IOCTL to firmware for device control and configuration. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_ioctl(enum pm_node_id nid, + uint32_t ioctl_id, + uint32_t arg1, + uint32_t arg2, + uint32_t *value) +{ + return pm_api_ioctl(nid, ioctl_id, arg1, arg2, value); +} + +/** + * fw_api_version() - Returns API version implemented in firmware. + * @id: API ID to check. + * @version: Returned supported API version. + * @len: Number of words to be returned. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status fw_api_version(uint32_t id, uint32_t *version, + uint32_t len) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD2(payload, PM_FEATURE_CHECK, id); + return pm_ipi_send_sync(primary_proc, payload, version, len); +} + +/** + * check_api_dependency() - API to check dependent EEMI API version. + * @id: EEMI API ID to check. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status check_api_dependency(uint8_t id) +{ + uint8_t i; + uint32_t version; + int ret; + + for (i = 0U; i < ARRAY_SIZE(api_dep_table); i++) { + if (api_dep_table[i].id == id) { + if (api_dep_table[i].api_id == 0U) { + break; + } + + ret = fw_api_version(api_dep_table[i].api_id, + &version, 1); + if (ret != PM_RET_SUCCESS) { + return ret; + } + + /* Check if fw version matches TF-A expected version */ + if (version != tfa_expected_ver_id[api_dep_table[i].api_id]) { + return PM_RET_ERROR_NOTSUPPORTED; + } + } + } + + return PM_RET_SUCCESS; +} + +/** + * feature_check_tfa() - These are API's completely implemented in TF-A. + * @api_id: API ID to check. + * @version: Returned supported API version. + * @bit_mask: Returned supported IOCTL id version. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status feature_check_tfa(uint32_t api_id, uint32_t *version, + uint32_t *bit_mask) +{ + switch (api_id) { + case PM_QUERY_DATA: + *version = TFA_API_QUERY_DATA_VERSION; + bit_mask[0] = (uint32_t)(PM_QUERY_FEATURE_BITMASK); + bit_mask[1] = (uint32_t)(PM_QUERY_FEATURE_BITMASK >> 32); + return PM_RET_SUCCESS; + case PM_GET_CALLBACK_DATA: + case PM_GET_TRUSTZONE_VERSION: + case PM_SET_SUSPEND_MODE: + *version = TFA_API_BASE_VERSION; + return PM_RET_SUCCESS; + default: + return PM_RET_ERROR_NO_FEATURE; + } +} + +/** + * get_tfa_version_for_partial_apis() - Return TF-A version for partially. + * implemented APIs + * @api_id: API ID to check. + * @version: Returned supported API version. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status get_tfa_version_for_partial_apis(uint32_t api_id, + uint32_t *version) +{ + switch (api_id) { + case PM_SELF_SUSPEND: + case PM_REQ_WAKEUP: + case PM_ABORT_SUSPEND: + case PM_SET_WAKEUP_SOURCE: + case PM_SYSTEM_SHUTDOWN: + case PM_GET_API_VERSION: + case PM_CLOCK_ENABLE: + case PM_CLOCK_DISABLE: + case PM_CLOCK_GETSTATE: + case PM_CLOCK_SETDIVIDER: + case PM_CLOCK_GETDIVIDER: + case PM_CLOCK_SETPARENT: + case PM_CLOCK_GETPARENT: + case PM_PLL_SET_PARAMETER: + case PM_PLL_GET_PARAMETER: + case PM_PLL_SET_MODE: + case PM_PLL_GET_MODE: + case PM_REGISTER_ACCESS: + *version = TFA_API_BASE_VERSION; + return PM_RET_SUCCESS; + case PM_FEATURE_CHECK: + *version = FW_API_VERSION_2; + return PM_RET_SUCCESS; + default: + return PM_RET_ERROR_ARGS; + } +} + +/** + * feature_check_partial() - These are API's partially implemented in + * TF-A and firmware both. + * @api_id: API ID to check. + * @version: Returned supported API version. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status feature_check_partial(uint32_t api_id, + uint32_t *version) +{ + uint32_t status; + + switch (api_id) { + case PM_SELF_SUSPEND: + case PM_REQ_WAKEUP: + case PM_ABORT_SUSPEND: + case PM_SET_WAKEUP_SOURCE: + case PM_SYSTEM_SHUTDOWN: + case PM_GET_API_VERSION: + case PM_CLOCK_ENABLE: + case PM_CLOCK_DISABLE: + case PM_CLOCK_GETSTATE: + case PM_CLOCK_SETDIVIDER: + case PM_CLOCK_GETDIVIDER: + case PM_CLOCK_SETPARENT: + case PM_CLOCK_GETPARENT: + case PM_PLL_SET_PARAMETER: + case PM_PLL_GET_PARAMETER: + case PM_PLL_SET_MODE: + case PM_PLL_GET_MODE: + case PM_REGISTER_ACCESS: + case PM_FEATURE_CHECK: + status = check_api_dependency(api_id); + if (status != PM_RET_SUCCESS) { + return status; + } + return get_tfa_version_for_partial_apis(api_id, version); + default: + return PM_RET_ERROR_NO_FEATURE; + } +} + +/** + * pm_feature_check() - Returns the supported API version if supported. + * @api_id: API ID to check. + * @version: Returned supported API version. + * @bit_mask: Returned supported IOCTL id version. + * @len: Number of bytes to be returned in bit_mask variable. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *version, + uint32_t *bit_mask, uint8_t len) +{ + uint32_t ret_payload[PAYLOAD_ARG_CNT] = {0U}; + uint32_t status; + + /* Get API version implemented in TF-A */ + status = feature_check_tfa(api_id, version, bit_mask); + if (status != PM_RET_ERROR_NO_FEATURE) { + return status; + } + + /* Get API version implemented by firmware and TF-A both */ + status = feature_check_partial(api_id, version); + if (status != PM_RET_ERROR_NO_FEATURE) { + return status; + } + + /* Get API version implemented by firmware */ + status = fw_api_version(api_id, ret_payload, 3); + /* IOCTL call may return failure whose ID is not implemented in + * firmware but implemented in TF-A + */ + if ((api_id != PM_IOCTL) && (status != PM_RET_SUCCESS)) { + return status; + } + + *version = ret_payload[0]; + + /* Update IOCTL bit mask which are implemented in TF-A */ + if ((api_id == PM_IOCTL) || (api_id == PM_GET_OP_CHARACTERISTIC)) { + if (len < 2) { + return PM_RET_ERROR_ARGS; + } + bit_mask[0] = ret_payload[1]; + bit_mask[1] = ret_payload[2]; + if (api_id == PM_IOCTL) { + /* Get IOCTL's implemented by TF-A */ + status = tfa_ioctl_bitmask(bit_mask); + } + } else { + /* Requires for MISRA */ + } + + return status; +} + +/** + * pm_clock_get_max_divisor - PM call to get max divisor. + * @clock_id: Clock ID. + * @div_type: Divisor ID (TYPE_DIV1 or TYPE_DIV2). + * @max_div: Maximum supported divisor. + * + * This function is used by master to get maximum supported value. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_clock_get_max_divisor(uint32_t clock_id, + uint8_t div_type, + uint32_t *max_div) +{ + return pm_api_clock_get_max_divisor(clock_id, div_type, max_div); +} + +/** + * pm_clock_get_num_clocks - PM call to request number of clocks. + * @nclocks: Number of clocks. + * + * This function is used by master to get number of clocks. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_clock_get_num_clocks(uint32_t *nclocks) +{ + return pm_api_clock_get_num_clocks(nclocks); +} + +/** + * pm_clock_get_name() - PM call to request a clock's name. + * @clock_id: Clock ID. + * @name: Name of clock (max 16 bytes). + * + * This function is used by master to get nmae of clock specified + * by given clock ID. + * + */ +static void pm_clock_get_name(uint32_t clock_id, char *name) +{ + pm_api_clock_get_name(clock_id, name); +} + +/** + * pm_clock_get_topology() - PM call to request a clock's topology. + * @clock_id: Clock ID. + * @index: Topology index for next toplogy node. + * @topology: Buffer to store nodes in topology and flags. + * + * This function is used by master to get topology information for the + * clock specified by given clock ID. Each response would return 3 + * topology nodes. To get next nodes, caller needs to call this API with + * index of next node. Index starts from 0. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_clock_get_topology(uint32_t clock_id, + uint32_t index, + uint32_t *topology) +{ + return pm_api_clock_get_topology(clock_id, index, topology); +} + +/** + * pm_clock_get_fixedfactor_params() - PM call to request a clock's fixed factor + * parameters for fixed clock. + * @clock_id: Clock ID. + * @mul: Multiplication value. + * @div: Divisor value. + * + * This function is used by master to get fixed factor parameers for the + * fixed clock. This API is application only for the fixed clock. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_clock_get_fixedfactor_params(uint32_t clock_id, + uint32_t *mul, + uint32_t *div) +{ + return pm_api_clock_get_fixedfactor_params(clock_id, mul, div); +} + +/** + * pm_clock_get_parents() - PM call to request a clock's first 3 parents. + * @clock_id: Clock ID. + * @index: Index of next parent. + * @parents: Parents of the given clock. + * + * This function is used by master to get clock's parents information. + * This API will return 3 parents with a single response. To get other + * parents, master should call same API in loop with new parent index + * till error is returned. + * + * E.g First call should have index 0 which will return parents 0, 1 and + * 2. Next call, index should be 3 which will return parent 3,4 and 5 and + * so on. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_clock_get_parents(uint32_t clock_id, + uint32_t index, + uint32_t *parents) +{ + return pm_api_clock_get_parents(clock_id, index, parents); +} + +/** + * pm_clock_get_attributes() - PM call to request a clock's attributes. + * @clock_id: Clock ID. + * @attr: Clock attributes. + * + * This function is used by master to get clock's attributes + * (e.g. valid, clock type, etc). + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_clock_get_attributes(uint32_t clock_id, + uint32_t *attr) +{ + return pm_api_clock_get_attributes(clock_id, attr); +} + +/** + * pm_clock_gate() - Configure clock gate. + * @clock_id: Id of the clock to be configured. + * @enable: Flag 0=disable (gate the clock), !0=enable (activate the clock). + * + * Return: Error if an argument is not valid or status as returned by the + * PM controller (PMU). + * + */ +static enum pm_ret_status pm_clock_gate(uint32_t clock_id, + uint8_t enable) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + enum pm_api_id api_id; + + /* Check if clock ID is valid and return an error if it is not */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) { + return status; + } + + if (enable) { + api_id = PM_CLOCK_ENABLE; + } else { + api_id = PM_CLOCK_DISABLE; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, api_id, clock_id); + status = pm_ipi_send_sync(primary_proc, payload, NULL, 0); + + /* If action fails due to the lack of permissions filter the error */ + if (status == PM_RET_ERROR_ACCESS) { + status = PM_RET_SUCCESS; + } + + return status; +} + +/** + * pm_clock_enable() - Enable the clock for given id. + * @clock_id: Id of the clock to be enabled. + * + * This function is used by master to enable the clock + * including peripherals and PLL clocks. + * + * Return: Error if an argument is not valid or status as returned by the + * pm_clock_gate. + * + */ +enum pm_ret_status pm_clock_enable(uint32_t clock_id) +{ + struct pm_pll *pll; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll(clock_id); + if (pll) { + return pm_clock_pll_enable(pll); + } + + /* It's an on-chip clock, PMU should configure clock's gate */ + return pm_clock_gate(clock_id, 1); +} + +/** + * pm_clock_disable - Disable the clock for given id. + * @clock_id: Id of the clock to be disable. + * + * This function is used by master to disable the clock + * including peripherals and PLL clocks. + * + * Return: Error if an argument is not valid or status as returned by the + * pm_clock_gate + * + */ +enum pm_ret_status pm_clock_disable(uint32_t clock_id) +{ + struct pm_pll *pll; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll(clock_id); + if (pll) { + return pm_clock_pll_disable(pll); + } + + /* It's an on-chip clock, PMU should configure clock's gate */ + return pm_clock_gate(clock_id, 0); +} + +/** + * pm_clock_getstate - Get the clock state for given id. + * @clock_id: Id of the clock to be queried. + * @state: 1/0 (Enabled/Disabled). + * + * This function is used by master to get the state of clock + * including peripherals and PLL clocks. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_clock_getstate(uint32_t clock_id, + uint32_t *state) +{ + struct pm_pll *pll; + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll(clock_id); + if (pll) + return pm_clock_pll_get_state(pll, state); + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) { + return status; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, PM_CLOCK_GETSTATE, clock_id); + return pm_ipi_send_sync(primary_proc, payload, state, 1); +} + +/** + * pm_clock_setdivider - Set the clock divider for given id. + * @clock_id: Id of the clock. + * @divider: divider value. + * + * This function is used by master to set divider for any clock + * to achieve desired rate. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_clock_setdivider(uint32_t clock_id, + uint32_t divider) +{ + enum pm_ret_status status; + enum pm_node_id nid; + enum pm_clock_div_id div_id; + uint32_t payload[PAYLOAD_ARG_CNT]; + const uint32_t div0 = 0xFFFF0000; + const uint32_t div1 = 0x0000FFFF; + uint32_t val; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(clock_id, &nid); + if (status == PM_RET_SUCCESS) { + return pm_pll_set_parameter(nid, PM_PLL_PARAM_FBDIV, divider); + } + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) { + return status; + } + + if (div0 == (divider & div0)) { + div_id = PM_CLOCK_DIV0_ID; + val = divider & ~div0; + } else if (div1 == (divider & div1)) { + div_id = PM_CLOCK_DIV1_ID; + val = (divider & ~div1) >> 16; + } else { + return PM_RET_ERROR_ARGS; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, PM_CLOCK_SETDIVIDER, clock_id, div_id, val); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_clock_getdivider - Get the clock divider for given id. + * @clock_id: Id of the clock. + * @divider: divider value. + * + * This function is used by master to get divider values + * for any clock. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_clock_getdivider(uint32_t clock_id, + uint32_t *divider) +{ + enum pm_ret_status status; + enum pm_node_id nid; + uint32_t payload[PAYLOAD_ARG_CNT]; + uint32_t val; + + /* Get PLL node ID using PLL clock ID */ + status = pm_clock_get_pll_node_id(clock_id, &nid); + if (status == PM_RET_SUCCESS) { + return pm_pll_get_parameter(nid, PM_PLL_PARAM_FBDIV, divider); + } + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) { + return status; + } + + if (pm_clock_has_div(clock_id, PM_CLOCK_DIV0_ID)) { + /* Send request to the PMU to get div0 */ + PM_PACK_PAYLOAD3(payload, PM_CLOCK_GETDIVIDER, clock_id, + PM_CLOCK_DIV0_ID); + status = pm_ipi_send_sync(primary_proc, payload, &val, 1); + if (status != PM_RET_SUCCESS) { + return status; + } + *divider = val; + } + + if (pm_clock_has_div(clock_id, PM_CLOCK_DIV1_ID)) { + /* Send request to the PMU to get div1 */ + PM_PACK_PAYLOAD3(payload, PM_CLOCK_GETDIVIDER, clock_id, + PM_CLOCK_DIV1_ID); + status = pm_ipi_send_sync(primary_proc, payload, &val, 1); + if (status != PM_RET_SUCCESS) { + return status; + } + *divider |= val << 16; + } + + return status; +} + +/** + * pm_clock_setparent - Set the clock parent for given id. + * @clock_id: Id of the clock. + * @parent_index: Index of the parent clock into clock's parents array. + * + * This function is used by master to set parent for any clock. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_clock_setparent(uint32_t clock_id, + uint32_t parent_index) +{ + struct pm_pll *pll; + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll_by_related_clk(clock_id); + if (pll) { + return pm_clock_pll_set_parent(pll, clock_id, parent_index); + } + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) { + return status; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_CLOCK_SETPARENT, clock_id, parent_index); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_clock_getparent - Get the clock parent for given id. + * @clock_id: Id of the clock. + * @parent_index: parent index. + * + * This function is used by master to get parent index + * for any clock. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_clock_getparent(uint32_t clock_id, + uint32_t *parent_index) +{ + struct pm_pll *pll; + uint32_t payload[PAYLOAD_ARG_CNT]; + enum pm_ret_status status; + + /* First try to handle it as a PLL */ + pll = pm_clock_get_pll_by_related_clk(clock_id); + if (pll) { + return pm_clock_pll_get_parent(pll, clock_id, parent_index); + } + + /* Check if clock ID is a valid on-chip clock */ + status = pm_clock_id_is_valid(clock_id); + if (status != PM_RET_SUCCESS) { + return status; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, PM_CLOCK_GETPARENT, clock_id); + return pm_ipi_send_sync(primary_proc, payload, parent_index, 1); +} + +/** + * pm_pinctrl_get_num_pins - PM call to request number of pins. + * @npins: Number of pins. + * + * This function is used by master to get number of pins. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_pinctrl_get_num_pins(uint32_t *npins) +{ + return pm_api_pinctrl_get_num_pins(npins); +} + +/** + * pm_pinctrl_get_num_functions - PM call to request number of functions. + * @nfuncs: Number of functions. + * + * This function is used by master to get number of functions. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_pinctrl_get_num_functions(uint32_t *nfuncs) +{ + return pm_api_pinctrl_get_num_functions(nfuncs); +} + +/** + * pm_pinctrl_get_num_function_groups - PM call to request number of + * function groups. + * @fid: Id of function. + * @ngroups: Number of function groups. + * + * This function is used by master to get number of function groups specified + * by given function Id. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_pinctrl_get_num_function_groups(uint32_t fid, + uint32_t *ngroups) +{ + return pm_api_pinctrl_get_num_func_groups(fid, ngroups); +} + +/** + * pm_pinctrl_get_function_name - PM call to request function name. + * @fid: Id of function. + * @name: Name of function. + * + * This function is used by master to get name of function specified + * by given function Id. + * + */ +static void pm_pinctrl_get_function_name(uint32_t fid, char *name) +{ + pm_api_pinctrl_get_function_name(fid, name); +} + +/** + * pm_pinctrl_get_function_groups - PM call to request function groups. + * @fid: Id of function. + * @index: Index of next function groups. + * @groups: Function groups. + * + * This function is used by master to get function groups specified + * by given function Id. This API will return 6 function groups with + * a single response. To get other function groups, master should call + * same API in loop with new function groups index till error is returned. + * + * E.g First call should have index 0 which will return function groups + * 0, 1, 2, 3, 4 and 5. Next call, index should be 6 which will return + * function groups 6, 7, 8, 9, 10 and 11 and so on. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_pinctrl_get_function_groups(uint32_t fid, + uint32_t index, + uint16_t *groups) +{ + return pm_api_pinctrl_get_function_groups(fid, index, groups); +} + +/** + * pm_pinctrl_get_pin_groups - PM call to request pin groups. + * @pin_id: Id of pin. + * @index: Index of next pin groups. + * @groups: pin groups. + * + * This function is used by master to get pin groups specified + * by given pin Id. This API will return 6 pin groups with + * a single response. To get other pin groups, master should call + * same API in loop with new pin groups index till error is returned. + * + * E.g First call should have index 0 which will return pin groups + * 0, 1, 2, 3, 4 and 5. Next call, index should be 6 which will return + * pin groups 6, 7, 8, 9, 10 and 11 and so on. + * + * Return: Returns status, either success or error+reason. + * + */ +static enum pm_ret_status pm_pinctrl_get_pin_groups(uint32_t pin_id, + uint32_t index, + uint16_t *groups) +{ + return pm_api_pinctrl_get_pin_groups(pin_id, index, groups); +} + +/** + * pm_query_data() - PM API for querying firmware data. + * @qid: represents the query identifiers for PM. + * @arg1: Argument 1 to requested IOCTL call. + * @arg2: Argument 2 to requested IOCTL call. + * @arg3: Argument 3 to requested IOCTL call. + * @data: Returned output data. + * + * This function returns requested data. + * + */ +void pm_query_data(enum pm_query_ids qid, uint32_t arg1, uint32_t arg2, + uint32_t arg3, uint32_t *data) +{ + switch (qid) { + case PM_QID_CLOCK_GET_NAME: + pm_clock_get_name(arg1, (char *)data); + break; + case PM_QID_CLOCK_GET_TOPOLOGY: + data[0] = pm_clock_get_topology(arg1, arg2, &data[1]); + break; + case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS: + data[0] = pm_clock_get_fixedfactor_params(arg1, &data[1], + &data[2]); + break; + case PM_QID_CLOCK_GET_PARENTS: + data[0] = pm_clock_get_parents(arg1, arg2, &data[1]); + break; + case PM_QID_CLOCK_GET_ATTRIBUTES: + data[0] = pm_clock_get_attributes(arg1, &data[1]); + break; + case PM_QID_PINCTRL_GET_NUM_PINS: + data[0] = pm_pinctrl_get_num_pins(&data[1]); + break; + case PM_QID_PINCTRL_GET_NUM_FUNCTIONS: + data[0] = pm_pinctrl_get_num_functions(&data[1]); + break; + case PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS: + data[0] = pm_pinctrl_get_num_function_groups(arg1, &data[1]); + break; + case PM_QID_PINCTRL_GET_FUNCTION_NAME: + pm_pinctrl_get_function_name(arg1, (char *)data); + break; + case PM_QID_PINCTRL_GET_FUNCTION_GROUPS: + data[0] = pm_pinctrl_get_function_groups(arg1, arg2, + (uint16_t *)&data[1]); + break; + case PM_QID_PINCTRL_GET_PIN_GROUPS: + data[0] = pm_pinctrl_get_pin_groups(arg1, arg2, + (uint16_t *)&data[1]); + break; + case PM_QID_CLOCK_GET_NUM_CLOCKS: + data[0] = pm_clock_get_num_clocks(&data[1]); + break; + + case PM_QID_CLOCK_GET_MAX_DIVISOR: + data[0] = pm_clock_get_max_divisor(arg1, arg2, &data[1]); + break; + default: + data[0] = PM_RET_ERROR_ARGS; + WARN("Unimplemented query service call: 0x%x\n", qid); + break; + } +} + +enum pm_ret_status pm_sha_hash(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_SHA, address_high, address_low, + size, flags); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +enum pm_ret_status pm_rsa_core(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_RSA, address_high, address_low, + size, flags); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +enum pm_ret_status pm_secure_image(uint32_t address_low, + uint32_t address_high, + uint32_t key_lo, + uint32_t key_hi, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_IMAGE, address_high, address_low, + key_hi, key_lo); + return pm_ipi_send_sync(primary_proc, payload, value, 2); +} + +/** + * pm_fpga_read - Perform the fpga configuration readback. + * @reg_numframes: Configuration register offset (or) Number of frames to read. + * @address_low: lower 32-bit Linear memory space address. + * @address_high: higher 32-bit Linear memory space address. + * @readback_type: Type of fpga readback operation. + * 0 -- Configuration Register readback. + * 1 -- Configuration Data readback. + * @value: Value to read. + * + * This function provides access to the xilfpga library to read + * the PL configuration. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_fpga_read(uint32_t reg_numframes, + uint32_t address_low, + uint32_t address_high, + uint32_t readback_type, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_FPGA_READ, reg_numframes, address_low, + address_high, readback_type); + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/* + * pm_pll_set_parameter() - Set the PLL parameter value. + * @nid: Node id of the target PLL. + * @param_id: ID of the PLL parameter. + * @value: Parameter value to be set. + * + * Setting the parameter will have physical effect once the PLL mode is set to + * integer or fractional. + * + * Return: Error if an argument is not valid or status as returned by the + * PM controller (PMU). + * + */ +enum pm_ret_status pm_pll_set_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + uint32_t value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) { + return PM_RET_ERROR_ARGS; + } + + /* Check if parameter ID is valid and return an error if it's not */ + if (param_id >= PM_PLL_PARAM_MAX) { + return PM_RET_ERROR_ARGS; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, PM_PLL_SET_PARAMETER, nid, param_id, value); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pll_get_parameter() - Get the PLL parameter value. + * @nid: Node id of the target PLL. + * @param_id: ID of the PLL parameter. + * @value: Location to store the parameter value. + * + * Return: Error if an argument is not valid or status as returned by the + * PM controller (PMU). + * + */ +enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) { + return PM_RET_ERROR_ARGS; + } + + /* Check if parameter ID is valid and return an error if it's not */ + if (param_id >= PM_PLL_PARAM_MAX) { + return PM_RET_ERROR_ARGS; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_PLL_GET_PARAMETER, nid, param_id); + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/** + * pm_pll_set_mode() - Set the PLL mode. + * @nid: Node id of the target PLL. + * @mode: PLL mode to be set. + * + * If reset mode is set the PM controller will first bypass the PLL and then + * assert the reset. If integer or fractional mode is set the PM controller will + * ensure that the complete PLL programming sequence is satisfied. After this + * function returns success the PLL is locked and its bypass is deasserted. + * + * Return: Error if an argument is not valid or status as returned by the + * PM controller (PMU). + * + */ +enum pm_ret_status pm_pll_set_mode(enum pm_node_id nid, enum pm_pll_mode mode) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) { + return PM_RET_ERROR_ARGS; + } + + /* Check if PLL mode is valid */ + if (mode >= PM_PLL_MODE_MAX) { + return PM_RET_ERROR_ARGS; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_PLL_SET_MODE, nid, mode); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pll_get_mode() - Get the PLL mode. + * @nid: Node id of the target PLL. + * @mode: Location to store the mode of the PLL. + * + * Return: Error if an argument is not valid or status as returned by the + * PM controller (PMU). + * + */ +enum pm_ret_status pm_pll_get_mode(enum pm_node_id nid, enum pm_pll_mode *mode) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Check if given node ID is a PLL node */ + if (nid < NODE_APLL || nid > NODE_IOPLL) { + return PM_RET_ERROR_ARGS; + } + + /* Send request to the PMU */ + PM_PACK_PAYLOAD2(payload, PM_PLL_GET_MODE, nid); + return pm_ipi_send_sync(primary_proc, payload, mode, 1); +} + +/** + * pm_register_access() - PM API for register read/write access data. + * @register_access_id: Register_access_id which says register read/write. + * @address: Address of the register to be accessed. + * @mask: Mask value to be used while writing value. + * @value: Value to be written to register. + * @out: Returned output data. + * + * This function returns requested data. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_register_access(uint32_t register_access_id, + uint32_t address, + uint32_t mask, + uint32_t value, + uint32_t *out) +{ + enum pm_ret_status ret; + + if (((ZYNQMP_CSU_BASEADDR & address) != ZYNQMP_CSU_BASEADDR) && + ((CSUDMA_BASE & address) != CSUDMA_BASE) && + ((RSA_CORE_BASE & address) != RSA_CORE_BASE) && + ((PMU_GLOBAL_BASE & address) != PMU_GLOBAL_BASE)) { + return PM_RET_ERROR_ACCESS; + } + + switch (register_access_id) { + case CONFIG_REG_WRITE: + ret = pm_mmio_write(address, mask, value); + break; + case CONFIG_REG_READ: + ret = pm_mmio_read(address, out); + break; + default: + ret = PM_RET_ERROR_ARGS; + WARN("Unimplemented register_access call\n\r"); + break; + } + return ret; +} + +/** + * pm_efuse_access() - To program or read efuse bits. This function provides + * access to the xilskey library to program/read + * efuse bits. + * @address_low: lower 32-bit Linear memory space address. + * @address_high: higher 32-bit Linear memory space address. + * @value: Returned output value. + * + * Return: Returns status, either success or error+reason. + * + */ +enum pm_ret_status pm_efuse_access(uint32_t address_high, + uint32_t address_low, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD3(payload, PM_EFUSE_ACCESS, address_high, address_low); + + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} diff --git a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.h new file mode 100644 index 0000000..f69a3e7 --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_api_sys.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ZYNQMP_PM_API_SYS_H +#define ZYNQMP_PM_API_SYS_H + +#include <stdint.h> + +#include "pm_defs.h" +#include "zynqmp_pm_defs.h" + +enum pm_query_ids { + PM_QID_INVALID, + PM_QID_CLOCK_GET_NAME, + PM_QID_CLOCK_GET_TOPOLOGY, + PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS, + PM_QID_CLOCK_GET_PARENTS, + PM_QID_CLOCK_GET_ATTRIBUTES, + PM_QID_PINCTRL_GET_NUM_PINS, + PM_QID_PINCTRL_GET_NUM_FUNCTIONS, + PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS, + PM_QID_PINCTRL_GET_FUNCTION_NAME, + PM_QID_PINCTRL_GET_FUNCTION_GROUPS, + PM_QID_PINCTRL_GET_PIN_GROUPS, + PM_QID_CLOCK_GET_NUM_CLOCKS, + PM_QID_CLOCK_GET_MAX_DIVISOR, +}; + +enum pm_register_access_id { + CONFIG_REG_WRITE, + CONFIG_REG_READ, +}; + +/* + * Assigning of argument values into array elements. + */ +#define PM_PACK_PAYLOAD1(pl, arg0) { \ + pl[0] = (uint32_t)(arg0); \ +} + +#define PM_PACK_PAYLOAD2(pl, arg0, arg1) { \ + pl[1] = (uint32_t)(arg1); \ + PM_PACK_PAYLOAD1(pl, arg0); \ +} + +#define PM_PACK_PAYLOAD3(pl, arg0, arg1, arg2) { \ + pl[2] = (uint32_t)(arg2); \ + PM_PACK_PAYLOAD2(pl, arg0, arg1); \ +} + +#define PM_PACK_PAYLOAD4(pl, arg0, arg1, arg2, arg3) { \ + pl[3] = (uint32_t)(arg3); \ + PM_PACK_PAYLOAD3(pl, arg0, arg1, arg2); \ +} + +#define PM_PACK_PAYLOAD5(pl, arg0, arg1, arg2, arg3, arg4) { \ + pl[4] = (uint32_t)(arg4); \ + PM_PACK_PAYLOAD4(pl, arg0, arg1, arg2, arg3); \ +} + +#define PM_PACK_PAYLOAD6(pl, arg0, arg1, arg2, arg3, arg4, arg5) { \ + pl[5] = (uint32_t)(arg5); \ + PM_PACK_PAYLOAD5(pl, arg0, arg1, arg2, arg3, arg4); \ +} + +/********************************************************** + * System-level API function declarations + **********************************************************/ +enum pm_ret_status pm_req_suspend(enum pm_node_id target, + enum pm_request_ack ack, + uint32_t latency, + uint32_t state); + +enum pm_ret_status pm_self_suspend(enum pm_node_id nid, + uint32_t latency, + uint32_t state, + uintptr_t address); + +enum pm_ret_status pm_force_powerdown(enum pm_node_id target, + enum pm_request_ack ack); + +enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason); + +enum pm_ret_status pm_req_wakeup(enum pm_node_id target, + uint32_t set_address, + uintptr_t address, + enum pm_request_ack ack); + +enum pm_ret_status pm_set_wakeup_source(enum pm_node_id target, + enum pm_node_id wkup_node, + uint32_t enable); + +enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype); + +/* API functions for managing PM Slaves */ +enum pm_ret_status pm_req_node(enum pm_node_id nid, + uint32_t capabilities, + uint32_t qos, + enum pm_request_ack ack); + +enum pm_ret_status pm_set_requirement(enum pm_node_id nid, + uint32_t capabilities, + uint32_t qos, + enum pm_request_ack ack); + +/* Miscellaneous API functions */ +enum pm_ret_status pm_get_api_version(uint32_t *version); +enum pm_ret_status pm_get_node_status(enum pm_node_id nid, + uint32_t *ret_buff); + +/* Direct-Control API functions */ +enum pm_ret_status pm_mmio_write(uintptr_t address, + uint32_t mask, + uint32_t value); +enum pm_ret_status pm_mmio_read(uintptr_t address, uint32_t *value); +enum pm_ret_status pm_fpga_load(uint32_t address_low, + uint32_t address_high, + uint32_t size, + uint32_t flags); +enum pm_ret_status pm_fpga_get_status(uint32_t *value); + +enum pm_ret_status pm_get_chipid(uint32_t *value); +enum pm_ret_status pm_secure_rsaaes(uint32_t address_low, + uint32_t address_high, + uint32_t size, + uint32_t flags); +uint32_t pm_get_shutdown_scope(void); +enum pm_ret_status pm_get_callbackdata(uint32_t *data, size_t count); +enum pm_ret_status pm_ioctl(enum pm_node_id nid, + uint32_t ioctl_id, + uint32_t arg1, + uint32_t arg2, + uint32_t *value); +enum pm_ret_status pm_clock_enable(uint32_t clock_id); +enum pm_ret_status pm_clock_disable(uint32_t clock_id); +enum pm_ret_status pm_clock_getstate(uint32_t clock_id, + uint32_t *state); +enum pm_ret_status pm_clock_setdivider(uint32_t clock_id, + uint32_t divider); +enum pm_ret_status pm_clock_getdivider(uint32_t clock_id, + uint32_t *divider); +enum pm_ret_status pm_clock_setparent(uint32_t clock_id, + uint32_t parent_index); +enum pm_ret_status pm_clock_getparent(uint32_t clock_id, + uint32_t *parent_index); +void pm_query_data(enum pm_query_ids qid, uint32_t arg1, uint32_t arg2, + uint32_t arg3, uint32_t *data); +enum pm_ret_status pm_sha_hash(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags); +enum pm_ret_status pm_rsa_core(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags); +enum pm_ret_status pm_secure_image(uint32_t address_low, + uint32_t address_high, + uint32_t key_lo, + uint32_t key_hi, + uint32_t *value); +enum pm_ret_status pm_fpga_read(uint32_t reg_numframes, + uint32_t address_low, + uint32_t address_high, + uint32_t readback_type, + uint32_t *value); +enum pm_ret_status pm_aes_engine(uint32_t address_high, + uint32_t address_low, + uint32_t *value); +enum pm_ret_status pm_register_access(uint32_t register_access_id, + uint32_t address, + uint32_t mask, + uint32_t value, + uint32_t *out); +enum pm_ret_status pm_pll_set_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + uint32_t value); +enum pm_ret_status pm_pll_get_parameter(enum pm_node_id nid, + enum pm_pll_param param_id, + uint32_t *value); +enum pm_ret_status pm_pll_set_mode(enum pm_node_id nid, enum pm_pll_mode mode); +enum pm_ret_status pm_pll_get_mode(enum pm_node_id nid, enum pm_pll_mode *mode); +enum pm_ret_status pm_efuse_access(uint32_t address_high, + uint32_t address_low, uint32_t *value); +enum pm_ret_status pm_feature_check(uint32_t api_id, uint32_t *version, + uint32_t *bit_mask, uint8_t len); +enum pm_ret_status check_api_dependency(uint8_t id); + +#endif /* ZYNQMP_PM_API_SYS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_defs.h b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_defs.h new file mode 100644 index 0000000..af75c5c --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_defs.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* ZynqMP power management enums and defines */ + +#ifndef ZYNQMP_PM_DEFS_H +#define ZYNQMP_PM_DEFS_H + +/********************************************************************* + * Macro definitions + ********************************************************************/ + +/* + * Version number is a 32bit value, like: + * (PM_VERSION_MAJOR << 16) | PM_VERSION_MINOR + */ +#define PM_VERSION_MAJOR 1U +#define PM_VERSION_MINOR 1U + +#define PM_VERSION ((PM_VERSION_MAJOR << 16U) | PM_VERSION_MINOR) + +/* + * PM API versions + */ + +/* Expected version of firmware APIs */ +#define FW_API_BASE_VERSION (1U) +/* Expected version of firmware API for feature check */ +#define FW_API_VERSION_2 (2U) +/* Version of APIs implemented in TF-A */ +#define TFA_API_BASE_VERSION (1U) +/* Updating the QUERY_DATA API versioning as the bitmask functionality + * support is added in the v2.*/ +#define TFA_API_QUERY_DATA_VERSION (2U) + +/* Capabilities for RAM */ +#define PM_CAP_ACCESS 0x1U +#define PM_CAP_CONTEXT 0x2U + +/* APU processor states */ +#define PM_PROC_STATE_FORCEDOFF 0U +#define PM_PROC_STATE_ACTIVE 1U +#define PM_PROC_STATE_SLEEP 2U +#define PM_PROC_STATE_SUSPENDING 3U + +#define PM_SET_SUSPEND_MODE 0xa02 + +/********************************************************************* + * Enum definitions + ********************************************************************/ + +enum pm_node_id { + NODE_UNKNOWN = 0, + NODE_APU, + NODE_APU_0, + NODE_APU_1, + NODE_APU_2, + NODE_APU_3, + NODE_RPU, + NODE_RPU_0, + NODE_RPU_1, + NODE_PLD, + NODE_FPD, + NODE_OCM_BANK_0, + NODE_OCM_BANK_1, + NODE_OCM_BANK_2, + NODE_OCM_BANK_3, + NODE_TCM_0_A, + NODE_TCM_0_B, + NODE_TCM_1_A, + NODE_TCM_1_B, + NODE_L2, + NODE_GPU_PP_0, + NODE_GPU_PP_1, + NODE_USB_0, + NODE_USB_1, + NODE_TTC_0, + NODE_TTC_1, + NODE_TTC_2, + NODE_TTC_3, + NODE_SATA, + NODE_ETH_0, + NODE_ETH_1, + NODE_ETH_2, + NODE_ETH_3, + NODE_UART_0, + NODE_UART_1, + NODE_SPI_0, + NODE_SPI_1, + NODE_I2C_0, + NODE_I2C_1, + NODE_SD_0, + NODE_SD_1, + NODE_DP, + NODE_GDMA, + NODE_ADMA, + NODE_NAND, + NODE_QSPI, + NODE_GPIO, + NODE_CAN_0, + NODE_CAN_1, + NODE_EXTERN, + NODE_APLL, + NODE_VPLL, + NODE_DPLL, + NODE_RPLL, + NODE_IOPLL, + NODE_DDR, + NODE_IPI_APU, + NODE_IPI_RPU_0, + NODE_GPU, + NODE_PCIE, + NODE_PCAP, + NODE_RTC, + NODE_LPD, + NODE_VCU, + NODE_IPI_RPU_1, + NODE_IPI_PL_0, + NODE_IPI_PL_1, + NODE_IPI_PL_2, + NODE_IPI_PL_3, + NODE_PL, + NODE_GEM_TSU, + NODE_SWDT_0, + NODE_SWDT_1, + NODE_CSU, + NODE_PJTAG, + NODE_TRACE, + NODE_TESTSCAN, + NODE_PMU, + NODE_MAX, +}; + +enum pm_request_ack { + REQ_ACK_NO = 1, + REQ_ACK_BLOCKING, + REQ_ACK_NON_BLOCKING, +}; + +enum pm_suspend_reason { + SUSPEND_REASON_PU_REQ = 201, + SUSPEND_REASON_ALERT, + SUSPEND_REASON_SYS_SHUTDOWN, +}; + +enum pm_ram_state { + PM_RAM_STATE_OFF = 1, + PM_RAM_STATE_RETENTION, + PM_RAM_STATE_ON, +}; + +/** + * enum pm_boot_status - enum represents the boot status of the PM. + * @PM_INITIAL_BOOT: boot is a fresh system startup. + * @PM_RESUME: boot is a resume. + * @PM_BOOT_ERROR: error, boot cause cannot be identified. + * + */ +enum pm_boot_status { + PM_INITIAL_BOOT, + PM_RESUME, + PM_BOOT_ERROR, +}; + +/** + * enum pm_shutdown_type - enum represents the shutdown type of the PM. + * @PMF_SHUTDOWN_TYPE_SHUTDOWN: shutdown. + * @PMF_SHUTDOWN_TYPE_RESET: reset/reboot. + * @PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY: set the shutdown/reboot scope. + * + */ +enum pm_shutdown_type { + PMF_SHUTDOWN_TYPE_SHUTDOWN, + PMF_SHUTDOWN_TYPE_RESET, + PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY, +}; + +/** + * enum pm_shutdown_subtype - enum represents the shutdown subtype of the PM. + * @PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM: shutdown/reboot APU subsystem only. + * @PMF_SHUTDOWN_SUBTYPE_PS_ONLY: shutdown/reboot entire PS (but not PL). + * @PMF_SHUTDOWN_SUBTYPE_SYSTEM: shutdown/reboot entire system. + * + */ +enum pm_shutdown_subtype { + PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM, + PMF_SHUTDOWN_SUBTYPE_PS_ONLY, + PMF_SHUTDOWN_SUBTYPE_SYSTEM, +}; + +/** + * enum pm_pll_mode - enum represents the mode of the PLL. + * @PM_PLL_MODE_RESET: PLL is in reset (not locked). + * @PM_PLL_MODE_INTEGER: PLL is locked in integer mode. + * @PM_PLL_MODE_FRACTIONAL: PLL is locked in fractional mode. + * @PM_PLL_MODE_MAX: Represents the maximum mode value for the PLL. + */ +enum pm_pll_mode { + PM_PLL_MODE_RESET, + PM_PLL_MODE_INTEGER, + PM_PLL_MODE_FRACTIONAL, + PM_PLL_MODE_MAX, +}; + +/** + * enum pm_clock_div_id - enum represents the clock division identifiers in the + * PM. + * @PM_CLOCK_DIV0_ID: Clock divider 0. + * @PM_CLOCK_DIV1_ID: Clock divider 1. + */ +enum pm_clock_div_id { + PM_CLOCK_DIV0_ID, + PM_CLOCK_DIV1_ID, +}; + +#endif /* ZYNQMP_PM_DEFS_H */ diff --git a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.c new file mode 100644 index 0000000..5a6a9f8 --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Top-level SMC handler for ZynqMP power management calls and + * IPI setup functions for communication with PMU. + */ + +#include <errno.h> + +#include <arch_helpers.h> +#include <common/runtime_svc.h> +#include <drivers/arm/gicv2.h> +#include <lib/mmio.h> +#include <lib/spinlock.h> +#include <plat/common/platform.h> + +#include <plat_private.h> +#include "pm_client.h" +#include "pm_ipi.h" +#include "zynqmp_pm_api_sys.h" +#include "zynqmp_pm_defs.h" + +/* pm_up = !0 - UP, pm_up = 0 - DOWN */ +static int32_t pm_up, ipi_irq_flag; + +#if ZYNQMP_WDT_RESTART +static spinlock_t inc_lock; +static int active_cores = 0; +#endif + +/** + * typedef pm_ctx_t - Structure which contains data for power management. + * @api_version: version of PM API, must match with one on PMU side. + * @payload: payload array used to store received. + * data from ipi buffer registers. + * + */ +typedef struct { + uint32_t api_version; + uint32_t payload[PAYLOAD_ARG_CNT]; +} pm_ctx_t; + +static pm_ctx_t pm_ctx; + +#if ZYNQMP_WDT_RESTART +/** + * trigger_wdt_restart() - Trigger warm restart event to APU cores. + * + * This function triggers SGI for all active APU CPUs. SGI handler then + * power down CPU and call system reset. + * + */ +static void trigger_wdt_restart(void) +{ + uint32_t core_count = 0; + uint32_t core_status[3]; + uint32_t target_cpu_list = 0; + int i; + + for (i = 0; i < 4; i++) { + pm_get_node_status(NODE_APU_0 + i, core_status); + if (core_status[0] == 1) { + core_count++; + target_cpu_list |= (1 << i); + } + } + + spin_lock(&inc_lock); + active_cores = core_count; + spin_unlock(&inc_lock); + + INFO("Active Cores: %d\n", active_cores); + + for (i = PLATFORM_CORE_COUNT - 1; i >= 0; i--) { + if (target_cpu_list & (1 << i)) { + /* trigger SGI to active cores */ + plat_ic_raise_el3_sgi(ARM_IRQ_SEC_SGI_7, i); + } + } +} + +/** + * ttc_fiq_handler() - TTC Handler for timer event. + * @id: number of the highest priority pending interrupt of the type + * that this handler was registered for. + * @flags: security state, bit[0]. + * @handle: pointer to 'cpu_context' structure of the current CPU for the + * security state specified in the 'flags' parameter. + * @cookie: unused. + * + * Function registered as INTR_TYPE_EL3 interrupt handler. + * + * When WDT event is received in PMU, PMU needs to notify master to do cleanup + * if required. PMU sets up timer and starts timer to overflow in zero time upon + * WDT event. TF-A handles this timer event and takes necessary action required + * for warm restart. + * + * In presence of non-secure software layers (EL1/2) sets the interrupt + * at registered entrance in GIC and informs that PMU responded or demands + * action. + * + * Return: 0 on success. + * + */ +static uint64_t ttc_fiq_handler(uint32_t id, uint32_t flags, void *handle, + void *cookie) +{ + INFO("BL31: Got TTC FIQ\n"); + + plat_ic_end_of_interrupt(id); + + /* Clear TTC interrupt by reading interrupt register */ + mmio_read_32(TTC3_INTR_REGISTER_1); + + /* Disable the timer interrupts */ + mmio_write_32(TTC3_INTR_ENABLE_1, 0); + + trigger_wdt_restart(); + + return 0; +} + +/** + * zynqmp_sgi7_irq() - Handler for SGI7 IRQ. + * @id: number of the highest priority pending interrupt of the type + * that this handler was registered for. + * @flags: security state, bit[0]. + * @handle: pointer to 'cpu_context' structure of the current CPU for the + * security state specified in the 'flags' parameter. + * @cookie: unused. + * + * Function registered as INTR_TYPE_EL3 interrupt handler + * + * On receiving WDT event from PMU, TF-A generates SGI7 to all running CPUs. + * In response to SGI7 interrupt, each CPUs do clean up if required and last + * running CPU calls system restart. + * + * Return: This function does not return a value and it enters into wfi. + */ +static uint64_t __unused __dead2 zynqmp_sgi7_irq(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + int i; + uint32_t value; + + /* enter wfi and stay there */ + INFO("Entering wfi\n"); + + spin_lock(&inc_lock); + active_cores--; + + for (i = 0; i < 4; i++) { + mmio_write_32(BASE_GICD_BASE + GICD_CPENDSGIR + 4 * i, + 0xffffffff); + } + + dsb(); + + spin_unlock(&inc_lock); + + if (active_cores == 0) { + pm_mmio_read(PMU_GLOBAL_GEN_STORAGE4, &value); + value = (value & RESTART_SCOPE_MASK) >> RESTART_SCOPE_SHIFT; + pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET, value); + } + + /* enter wfi and stay there */ + while (1) + wfi(); +} + +/** + * pm_wdt_restart_setup() - Setup warm restart interrupts. + * + * Return: Returns status, 0 on success or error+reason. + * + * This function sets up handler for SGI7 and TTC interrupts + * used for warm restart. + */ +static int pm_wdt_restart_setup(void) +{ + int ret; + + /* register IRQ handler for SGI7 */ + ret = request_intr_type_el3(ARM_IRQ_SEC_SGI_7, zynqmp_sgi7_irq); + if (ret) { + WARN("BL31: registering SGI7 interrupt failed\n"); + goto err; + } + + ret = request_intr_type_el3(IRQ_TTC3_1, ttc_fiq_handler); + if (ret) + WARN("BL31: registering TTC3 interrupt failed\n"); + +err: + return ret; +} +#endif + +/** + * 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 ZynqMP power management for + * communicaton with PMU. + * + * Called from sip_svc_setup initialization function with the + * rt_svc_init signature. + * + */ +int32_t pm_setup(void) +{ + enum pm_ret_status err; + + pm_ipi_init(primary_proc); + + err = pm_get_api_version(&pm_ctx.api_version); + if (err != PM_RET_SUCCESS) { + ERROR("BL31: Failed to read Platform Management API version. " + "Return: %d\n", err); + return -EINVAL; + } + if (pm_ctx.api_version < PM_VERSION) { + ERROR("BL31: Platform Management API version error. Expected: " + "v%d.%d - Found: v%d.%d\n", PM_VERSION_MAJOR, + PM_VERSION_MINOR, pm_ctx.api_version >> 16, + pm_ctx.api_version & 0xFFFFU); + return -EINVAL; + } + + int32_t status = 0, ret = 0; +#if ZYNQMP_WDT_RESTART + status = pm_wdt_restart_setup(); + if (status) + WARN("BL31: warm-restart setup failed\n"); +#endif + + if (status >= 0) { + INFO("BL31: PM Service Init Complete: API v%d.%d\n", + PM_VERSION_MAJOR, PM_VERSION_MINOR); + ret = 0; + } else { + INFO("BL31: PM Service Init Failed, Error Code %d!\n", status); + ret = status; + } + + pm_up = !status; + + return ret; +} + +/** + * pm_smc_handler() - SMC handler for PM-API calls coming from EL1/EL2. + * @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. + * + * 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. + * + * Return: Unused. + * + */ +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) +{ + enum pm_ret_status ret; + uint32_t payload[PAYLOAD_ARG_CNT]; + + uint32_t pm_arg[5]; + uint32_t result[PAYLOAD_ARG_CNT] = {0}; + uint32_t api_id; + + /* Handle case where PM wasn't initialized properly */ + if (pm_up == 0) + SMC_RET1(handle, SMC_UNK); + + pm_arg[0] = (uint32_t)x1; + pm_arg[1] = (uint32_t)(x1 >> 32); + pm_arg[2] = (uint32_t)x2; + pm_arg[3] = (uint32_t)(x2 >> 32); + pm_arg[4] = (uint32_t)x3; + + api_id = smc_fid & FUNCID_NUM_MASK; + + switch (api_id) { + /* PM API Functions */ + case PM_SELF_SUSPEND: + ret = pm_self_suspend(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_REQ_SUSPEND: + ret = pm_req_suspend(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_REQ_WAKEUP: + { + /* Use address flag is encoded in the 1st bit of the low-word */ + uint32_t set_addr = pm_arg[1] & 0x1U; + uint64_t address = (uint64_t)pm_arg[2] << 32U; + + address |= pm_arg[1] & (~0x1U); + ret = pm_req_wakeup(pm_arg[0], set_addr, address, + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + } + + case PM_FORCE_POWERDOWN: + ret = pm_force_powerdown(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_ABORT_SUSPEND: + ret = pm_abort_suspend(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SET_WAKEUP_SOURCE: + ret = pm_set_wakeup_source(pm_arg[0], pm_arg[1], pm_arg[2]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SYSTEM_SHUTDOWN: + ret = pm_system_shutdown(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_REQ_NODE: + ret = pm_req_node(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SET_REQUIREMENT: + ret = pm_set_requirement(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_GET_API_VERSION: + if (ipi_irq_flag == 0U) { + /* + * 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); + ipi_irq_flag = 1U; + } + SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS | + ((uint64_t)pm_ctx.api_version << 32)); + case PM_FPGA_LOAD: + ret = pm_fpga_load(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_FPGA_GET_STATUS: + { + uint32_t value = 0U; + + ret = pm_fpga_get_status(&value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_SECURE_RSA_AES: + ret = pm_secure_rsaaes(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_GET_CALLBACK_DATA: + ret = pm_get_callbackdata(result, ARRAY_SIZE(result)); + if (ret != PM_RET_SUCCESS) { + result[0] = ret; + } + + SMC_RET2(handle, + (uint64_t)result[0] | ((uint64_t)result[1] << 32), + (uint64_t)result[2] | ((uint64_t)result[3] << 32)); + case PM_IOCTL: + { + uint32_t value = 0U; + + ret = pm_ioctl(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_QUERY_DATA: + { + uint32_t data[4] = { 0 }; + + pm_query_data(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], data); + SMC_RET2(handle, (uint64_t)data[0] | ((uint64_t)data[1] << 32), + (uint64_t)data[2] | ((uint64_t)data[3] << 32)); + } + + case PM_CLOCK_ENABLE: + ret = pm_clock_enable(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_DISABLE: + ret = pm_clock_disable(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_GETSTATE: + { + uint32_t value = 0U; + + ret = pm_clock_getstate(pm_arg[0], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_CLOCK_SETDIVIDER: + ret = pm_clock_setdivider(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_GETDIVIDER: + { + uint32_t value = 0U; + + ret = pm_clock_getdivider(pm_arg[0], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_CLOCK_SETPARENT: + ret = pm_clock_setparent(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_GETPARENT: + { + uint32_t value = 0U; + + ret = pm_clock_getparent(pm_arg[0], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + } + + case PM_GET_TRUSTZONE_VERSION: + SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS | + ((uint64_t)ZYNQMP_TZ_VERSION << 32U)); + + case PM_SET_SUSPEND_MODE: + ret = pm_set_suspend_mode(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SECURE_SHA: + ret = pm_sha_hash(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SECURE_RSA: + ret = pm_rsa_core(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SECURE_IMAGE: + { + ret = pm_secure_image(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], &result[0]); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U), + result[1]); + } + + case PM_FPGA_READ: + { + uint32_t value = 0U; + + ret = pm_fpga_read(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3], + &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + } + + case PM_SECURE_AES: + { + uint32_t value = 0U; + + ret = pm_aes_engine(pm_arg[0], pm_arg[1], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + } + + case PM_PLL_SET_PARAMETER: + ret = pm_pll_set_parameter(pm_arg[0], pm_arg[1], pm_arg[2]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_PLL_GET_PARAMETER: + { + uint32_t value = 0U; + + ret = pm_pll_get_parameter(pm_arg[0], pm_arg[1], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value << 32U)); + } + + case PM_PLL_SET_MODE: + ret = pm_pll_set_mode(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_PLL_GET_MODE: + { + uint32_t mode = 0U; + + ret = pm_pll_get_mode(pm_arg[0], &mode); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)mode << 32U)); + } + + case PM_REGISTER_ACCESS: + { + uint32_t value = 0U; + + ret = pm_register_access(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + } + + case PM_EFUSE_ACCESS: + { + uint32_t value = 0U; + +#if defined(ZYNQMP_SECURE_EFUSES) + if (is_caller_non_secure(flags)) { + SMC_RET1(handle, + (((uint64_t)PM_RET_ERROR_NOT_ENABLED) << 32U) | + (uint64_t)PM_RET_ERROR_ACCESS); + } +#endif + ret = pm_efuse_access(pm_arg[0], pm_arg[1], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32U); + } + + case PM_FPGA_GET_VERSION: + case PM_FPGA_GET_FEATURE_LIST: + { + uint32_t ret_payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD5(payload, smc_fid & FUNCID_NUM_MASK, + pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3]); + ret = pm_ipi_send_sync(primary_proc, payload, ret_payload, 3U); + SMC_RET2(handle, (uint64_t)ret | (uint64_t)ret_payload[0] << 32U, + (uint64_t)ret_payload[1] | (uint64_t)ret_payload[2] << 32U); + } + + case PM_FEATURE_CHECK: + { + uint32_t version = 0; + uint32_t bit_mask[2] = {0}; + + ret = pm_feature_check(pm_arg[0], &version, bit_mask, + ARRAY_SIZE(bit_mask)); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)version << 32U), + (uint64_t)bit_mask[0] | ((uint64_t)bit_mask[1] << 32U)); + } + + default: + /* Send request to the PMU */ + PM_PACK_PAYLOAD6(payload, api_id, pm_arg[0], pm_arg[1], + pm_arg[2], pm_arg[3], pm_arg[4]); + ret = pm_ipi_send_sync(primary_proc, payload, result, + PAYLOAD_ARG_CNT); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32U), + (uint64_t)result[1] | ((uint64_t)result[2] << 32U)); + } +} diff --git a/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.h b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.h new file mode 100644 index 0000000..bef72b6 --- /dev/null +++ b/plat/xilinx/zynqmp/pm_service/zynqmp_pm_svc_main.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2013-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 ZYNQMP_PM_SVC_MAIN_H +#define ZYNQMP_PM_SVC_MAIN_H + +#include "pm_common.h" + +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); +#endif /* ZYNQMP_PM_SVC_MAIN_H */ diff --git a/plat/xilinx/zynqmp/sip_svc_setup.c b/plat/xilinx/zynqmp/sip_svc_setup.c new file mode 100644 index 0000000..6a8555e --- /dev/null +++ b/plat/xilinx/zynqmp/sip_svc_setup.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Top level SMC handler for SiP calls. Dispatch PM calls to PM SMC handler. */ + +#include <inttypes.h> + +#include <common/runtime_svc.h> +#include <tools_share/uuid.h> + +#include <custom_svc.h> +#include "ipi_mailbox_svc.h" +#include "pm_defs.h" +#include "zynqmp_pm_svc_main.h" + +/* SMC function IDs for SiP Service queries */ +#define ZYNQMP_SIP_SVC_CALL_COUNT U(0x8200ff00) +#define ZYNQMP_SIP_SVC_UID U(0x8200ff01) +#define ZYNQMP_SIP_SVC_VERSION U(0x8200ff03) + +/* SiP Service Calls version numbers */ +#define SIP_SVC_VERSION_MAJOR 0 +#define SIP_SVC_VERSION_MINOR 1 + +/* These macros are used to identify PM, IPI calls from the SMC function ID */ +#define SIP_FID_MASK GENMASK(23, 16) +#define XLNX_FID_MASK GENMASK(23, 12) +#define PM_FID_VALUE 0u +#define IPI_FID_VALUE 0x1000u +#define is_pm_fid(_fid) (((_fid) & XLNX_FID_MASK) == PM_FID_VALUE) +#define is_ipi_fid(_fid) (((_fid) & XLNX_FID_MASK) == IPI_FID_VALUE) + +/* SiP Service UUID */ +DEFINE_SVC_UUID2(zynqmp_sip_uuid, + 0x5c9b1b2a, 0x0586, 0x2340, 0xa6, 0x1b, + 0xb9, 0x25, 0x82, 0x2d, 0xe3, 0xa5); + +/** + * sip_svc_setup() - Setup SiP Service. + * + * Return: On success, the initialization function must return 0. + * Any other return value will cause the framework to ignore + * the service. + * + * Invokes PM setup. + */ +static int32_t sip_svc_setup(void) +{ + /* PM implementation as SiP Service */ + return pm_setup(); +} + +/** + * sip_svc_smc_handler() - Top-level SiP Service SMC handler + * @smc_fid: Function Identifier. + * @x1: SMC64 Arguments 1 from kernel. + * @x2: SMC64 Arguments 2 from kernel. + * @x3: SMC64 Arguments 3 from kernel(upper 32-bits). + * @x4: SMC64 Arguments 4 from kernel. + * @cookie: Unused + * @handle: Pointer to caller's context structure. + * @flags: SECURE_FLAG or NON_SECURE_FLAG. + * + * Handler for all SiP SMC calls. Handles standard SIP requests + * and calls PM SMC handler if the call is for a PM-API function. + * + * Return: Unused. + */ +static uintptr_t sip_svc_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + VERBOSE("SMCID: 0x%08x, x1: 0x%016" PRIx64 ", x2: 0x%016" PRIx64 ", x3: 0x%016" PRIx64 ", x4: 0x%016" PRIx64 "\n", + smc_fid, x1, x2, x3, x4); + + if (smc_fid & SIP_FID_MASK) { + WARN("SMC out of SiP assinged range: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } + + /* Let PM SMC handler deal with PM-related requests */ + if (is_pm_fid(smc_fid)) { + return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } + + /* Let IPI SMC handler deal with IPI-related requests */ + if (is_ipi_fid(smc_fid)) { + return ipi_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } + + switch (smc_fid) { + case ZYNQMP_SIP_SVC_CALL_COUNT: + /* PM functions + default functions */ + SMC_RET1(handle, PM_API_MAX + 2); + + case ZYNQMP_SIP_SVC_UID: + SMC_UUID_RET(handle, zynqmp_sip_uuid); + + case ZYNQMP_SIP_SVC_VERSION: + SMC_RET2(handle, SIP_SVC_VERSION_MAJOR, SIP_SVC_VERSION_MINOR); + + case ZYNQMP_SIP_SVC_CUSTOM: + case ZYNQMP_SIP_SVC64_CUSTOM: + return custom_smc_handler(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); + + default: + WARN("Unimplemented SiP Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} + +/* Register PM Service Calls as runtime service */ +DECLARE_RT_SVC( + sip_svc, + OEN_SIP_START, + OEN_SIP_END, + (uint8_t)SMC_TYPE_FAST, + sip_svc_setup, + sip_svc_smc_handler); diff --git a/plat/xilinx/zynqmp/tsp/tsp-zynqmp.mk b/plat/xilinx/zynqmp/tsp/tsp-zynqmp.mk new file mode 100644 index 0000000..1d6366f --- /dev/null +++ b/plat/xilinx/zynqmp/tsp/tsp-zynqmp.mk @@ -0,0 +1,10 @@ +# +# Copyright (c) 2014, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +# TSP source files specific to ZynqMP platform + +PLAT_XILINX_COMMON := plat/xilinx/common/ + +include ${PLAT_XILINX_COMMON}/tsp/tsp.mk diff --git a/plat/xilinx/zynqmp/zynqmp_ehf.c b/plat/xilinx/zynqmp/zynqmp_ehf.c new file mode 100644 index 0000000..18c3749 --- /dev/null +++ b/plat/xilinx/zynqmp/zynqmp_ehf.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (c) Siemens AG, 2020-2021 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <bl31/ehf.h> + +#include <platform_def.h> + +/* + * Enumeration of priority levels on ARM platforms. + */ +ehf_pri_desc_t zynqmp_exceptions[] = { + /* Critical priority SDEI */ + EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_SDEI_CRITICAL_PRI), + + /* Normal priority SDEI */ + EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_SDEI_NORMAL_PRI), +}; + +/* Plug in ARM exceptions to Exception Handling Framework. */ +EHF_REGISTER_PRIORITIES(zynqmp_exceptions, ARRAY_SIZE(zynqmp_exceptions), PLAT_PRI_BITS); diff --git a/plat/xilinx/zynqmp/zynqmp_ipi.c b/plat/xilinx/zynqmp/zynqmp_ipi.c new file mode 100644 index 0000000..439bd6f --- /dev/null +++ b/plat/xilinx/zynqmp/zynqmp_ipi.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2022, Xilinx, Inc. All rights reserved. + * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Zynq UltraScale+ MPSoC IPI agent registers access management + */ + +#include <lib/utils_def.h> +#include <ipi.h> +#include <plat_ipi.h> + +/* Zynqmp ipi configuration table */ +static const struct ipi_config zynqmp_ipi_table[] = { + /* APU IPI */ + { + .ipi_bit_mask = 0x1, + .ipi_reg_base = 0xFF300000U, + .secure_only = 0, + }, + /* RPU0 IPI */ + { + .ipi_bit_mask = 0x100, + .ipi_reg_base = 0xFF310000U, + .secure_only = 0, + }, + /* RPU1 IPI */ + { + .ipi_bit_mask = 0x200, + .ipi_reg_base = 0xFF320000U, + .secure_only = 0, + }, + /* PMU0 IPI */ + { + .ipi_bit_mask = 0x10000, + .ipi_reg_base = 0xFF330000U, + .secure_only = IPI_SECURE_MASK, + }, + /* PMU1 IPI */ + { + .ipi_bit_mask = 0x20000, + .ipi_reg_base = 0xFF331000U, + .secure_only = 0, + }, + /* PMU2 IPI */ + { + .ipi_bit_mask = 0x40000, + .ipi_reg_base = 0xFF332000U, + .secure_only = IPI_SECURE_MASK, + }, + /* PMU3 IPI */ + { + .ipi_bit_mask = 0x80000, + .ipi_reg_base = 0xFF333000U, + .secure_only = IPI_SECURE_MASK, + }, + /* PL0 IPI */ + { + .ipi_bit_mask = 0x1000000, + .ipi_reg_base = 0xFF340000U, + .secure_only = 0, + }, + /* PL1 IPI */ + { + .ipi_bit_mask = 0x2000000, + .ipi_reg_base = 0xFF350000U, + .secure_only = 0, + }, + /* PL2 IPI */ + { + .ipi_bit_mask = 0x4000000, + .ipi_reg_base = 0xFF360000U, + .secure_only = 0, + }, + /* PL3 IPI */ + { + .ipi_bit_mask = 0x8000000, + .ipi_reg_base = 0xFF370000U, + .secure_only = 0, + }, +}; + +/** + * zynqmp_ipi_config_table_init() - Initialize ZynqMP IPI configuration data. + * + */ +void zynqmp_ipi_config_table_init(void) +{ + ipi_config_table_init(zynqmp_ipi_table, ARRAY_SIZE(zynqmp_ipi_table)); +} diff --git a/plat/xilinx/zynqmp/zynqmp_sdei.c b/plat/xilinx/zynqmp/zynqmp_sdei.c new file mode 100644 index 0000000..ce68e6a --- /dev/null +++ b/plat/xilinx/zynqmp/zynqmp_sdei.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) Siemens AG, 2020-2021 + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* SDEI configuration for ARM platforms */ + +#include <bl31/ehf.h> +#include <common/debug.h> +#include <plat/common/platform.h> +#include <services/sdei.h> + +#include <platform_def.h> + +int arm_validate_ns_entrypoint(uintptr_t entrypoint) +{ + uint64_t base = BL31_BASE; + uint64_t limit = BL31_LIMIT; + + return (entrypoint < base || entrypoint > limit) ? 0 : -1; +} + +/* Private event mappings */ +static sdei_ev_map_t zynqmp_sdei_private[] = { + SDEI_DEFINE_EVENT_0(ZYNQMP_SDEI_SGI_PRIVATE), +}; + +/* Shared event mappings */ +static sdei_ev_map_t zynqmp_sdei_shared[] = { +}; + +void plat_sdei_setup(void) +{ + INFO("SDEI platform setup\n"); +} + +/* Export ARM SDEI events */ +REGISTER_SDEI_MAP(zynqmp_sdei_private, zynqmp_sdei_shared); |