diff --git a/drivers/arm/css/scp/css_bom_bootloader.c b/drivers/arm/css/scp/css_bom_bootloader.c new file mode 100644 index 0000000..1fc1270 --- /dev/null +++ b/drivers/arm/css/scp/css_bom_bootloader.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* ID of the MHU slot used for the BOM protocol */ +#define BOM_MHU_SLOT_ID 0 + +/* Boot commands sent from AP -> SCP */ +#define BOOT_CMD_INFO 0x00 +#define BOOT_CMD_DATA 0x01 + +/* BOM command header */ +typedef struct { + uint32_t id : 8; + uint32_t reserved : 24; +} bom_cmd_t; + +typedef struct { + uint32_t image_size; + uint32_t checksum; +} cmd_info_payload_t; + +/* + * Unlike the SCPI protocol, the boot protocol uses the same memory region + * for both AP -> SCP and SCP -> AP transfers; define the address of this... + */ +#define BOM_SHARED_MEM PLAT_CSS_SCP_COM_SHARED_MEM_BASE +#define BOM_CMD_HEADER ((bom_cmd_t *) BOM_SHARED_MEM) +#define BOM_CMD_PAYLOAD ((void *) (BOM_SHARED_MEM + sizeof(bom_cmd_t))) + +typedef struct { + /* Offset from the base address of the Trusted RAM */ + uint32_t offset; + uint32_t block_size; +} cmd_data_payload_t; + +/* + * All CSS platforms load SCP_BL2/SCP_BL2U just below BL2 (this is where BL31 + * usually resides except when ARM_BL31_IN_DRAM is + * set). Ensure that SCP_BL2/SCP_BL2U do not overflow into shared RAM and + * the tb_fw_config. + */ +CASSERT(SCP_BL2_LIMIT <= BL2_BASE, assert_scp_bl2_overwrite_bl2); +CASSERT(SCP_BL2U_LIMIT <= BL2_BASE, assert_scp_bl2u_overwrite_bl2); + +CASSERT(SCP_BL2_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2_overflow); +CASSERT(SCP_BL2U_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2u_overflow); + +static void scp_boot_message_start(void) +{ + mhu_secure_message_start(BOM_MHU_SLOT_ID); +} + +static void scp_boot_message_send(size_t payload_size) +{ + /* Ensure that any write to the BOM payload area is seen by SCP before + * we write to the MHU register. If these 2 writes were reordered by + * the CPU then SCP would read stale payload data */ + dmbst(); + + /* Send command to SCP */ + mhu_secure_message_send(BOM_MHU_SLOT_ID); +} + +static uint32_t scp_boot_message_wait(size_t size) +{ + uint32_t mhu_status; + + mhu_status = mhu_secure_message_wait(); + + /* Expect an SCP Boot Protocol message, reject any other protocol */ + if (mhu_status != (1 << BOM_MHU_SLOT_ID)) { + ERROR("MHU: Unexpected protocol (MHU status: 0x%x)\n", + mhu_status); + panic(); + } + + /* Ensure that any read to the BOM payload area is done after reading + * the MHU register. If these 2 reads were reordered then the CPU would + * read invalid payload data */ + dmbld(); + + return *(uint32_t *) BOM_SHARED_MEM; +} + +static void scp_boot_message_end(void) +{ + mhu_secure_message_end(BOM_MHU_SLOT_ID); +} + +int css_scp_boot_image_xfer(void *image, unsigned int image_size) +{ + uint32_t response; + uint32_t checksum; + cmd_info_payload_t *cmd_info_payload; + cmd_data_payload_t *cmd_data_payload; + + assert((uintptr_t) image == SCP_BL2_BASE); + + if ((image_size == 0) || (image_size % 4 != 0)) { + ERROR("Invalid size for the SCP_BL2 image. Must be a multiple of " + "4 bytes and not zero (current size = 0x%x)\n", + image_size); + return -1; + } + + /* Extract the checksum from the image */ + checksum = *(uint32_t *) image; + image = (char *) image + sizeof(checksum); + image_size -= sizeof(checksum); + + mhu_secure_init(); + + VERBOSE("Send info about the SCP_BL2 image to be transferred to SCP\n"); + + /* + * Send information about the SCP firmware image about to be transferred + * to SCP + */ + scp_boot_message_start(); + + BOM_CMD_HEADER->id = BOOT_CMD_INFO; + cmd_info_payload = BOM_CMD_PAYLOAD; + cmd_info_payload->image_size = image_size; + cmd_info_payload->checksum = checksum; + + scp_boot_message_send(sizeof(*cmd_info_payload)); +#if CSS_DETECT_PRE_1_7_0_SCP + { + const uint32_t deprecated_scp_nack_cmd = 0x404; + uint32_t mhu_status; + + VERBOSE("Detecting SCP version incompatibility\n"); + + mhu_status = mhu_secure_message_wait(); + if (mhu_status == deprecated_scp_nack_cmd) { + ERROR("Detected an incompatible version of the SCP firmware.\n"); + ERROR("Only versions from v1.7.0 onwards are supported.\n"); + ERROR("Please update the SCP firmware.\n"); + return -1; + } + + VERBOSE("SCP version looks OK\n"); + } +#endif /* CSS_DETECT_PRE_1_7_0_SCP */ + response = scp_boot_message_wait(sizeof(response)); + scp_boot_message_end(); + + if (response != 0) { + ERROR("SCP BOOT_CMD_INFO returned error %u\n", response); + return -1; + } + + VERBOSE("Transferring SCP_BL2 image to SCP\n"); + + /* Transfer SCP_BL2 image to SCP */ + scp_boot_message_start(); + + BOM_CMD_HEADER->id = BOOT_CMD_DATA; + cmd_data_payload = BOM_CMD_PAYLOAD; + cmd_data_payload->offset = (uintptr_t) image - ARM_TRUSTED_SRAM_BASE; + cmd_data_payload->block_size = image_size; + + scp_boot_message_send(sizeof(*cmd_data_payload)); + response = scp_boot_message_wait(sizeof(response)); + scp_boot_message_end(); + + if (response != 0) { + ERROR("SCP BOOT_CMD_DATA returned error %u\n", response); + return -1; + } + + return 0; +} + +int css_scp_boot_ready(void) +{ + VERBOSE("Waiting for SCP to signal it is ready to go on\n"); + + /* Wait for SCP to signal it's ready */ + return scpi_wait_ready(); +} diff --git a/drivers/arm/css/scp/css_pm_scmi.c b/drivers/arm/css/scp/css_pm_scmi.c new file mode 100644 index 0000000..1966c44 --- /dev/null +++ b/drivers/arm/css/scp/css_pm_scmi.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This file implements the SCP helper functions using SCMI protocol. + */ + +/* + * SCMI power state parameter bit field encoding for ARM CSS platforms. + * + * 31 20 19 16 15 12 11 8 7 4 3 0 + * +-------------------------------------------------------------+ + * | SBZ | Max level | Level 3 | Level 2 | Level 1 | Level 0 | + * | | | state | state | state | state | + * +-------------------------------------------------------------+ + * + * `Max level` encodes the highest level that has a valid power state + * encoded in the power state. + */ +#define SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT 16 +#define SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH 4 +#define SCMI_PWR_STATE_MAX_PWR_LVL_MASK \ + ((1 << SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH) - 1) +#define SCMI_SET_PWR_STATE_MAX_PWR_LVL(_power_state, _max_level) \ + (_power_state) |= ((_max_level) & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)\ + << SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT +#define SCMI_GET_PWR_STATE_MAX_PWR_LVL(_power_state) \ + (((_power_state) >> SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT) \ + & SCMI_PWR_STATE_MAX_PWR_LVL_MASK) + +#define SCMI_PWR_STATE_LVL_WIDTH 4 +#define SCMI_PWR_STATE_LVL_MASK \ + ((1 << SCMI_PWR_STATE_LVL_WIDTH) - 1) +#define SCMI_SET_PWR_STATE_LVL(_power_state, _level, _level_state) \ + (_power_state) |= ((_level_state) & SCMI_PWR_STATE_LVL_MASK) \ + << (SCMI_PWR_STATE_LVL_WIDTH * (_level)) +#define SCMI_GET_PWR_STATE_LVL(_power_state, _level) \ + (((_power_state) >> (SCMI_PWR_STATE_LVL_WIDTH * (_level))) & \ + SCMI_PWR_STATE_LVL_MASK) + +/* + * The SCMI power state enumeration for a power domain level + */ +typedef enum { + scmi_power_state_off = 0, + scmi_power_state_on = 1, + scmi_power_state_sleep = 2, +} scmi_power_state_t; + +/* + * The global handle for invoking the SCMI driver APIs after the driver + * has been initialized. + */ +static void *scmi_handle; + +/* The SCMI channel global object */ +static scmi_channel_t channel; + +ARM_SCMI_INSTANTIATE_LOCK; + +/* + * Helper function to suspend a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_suspend(const struct psci_power_state *target_state) +{ + int ret; + + /* At least power domain level 0 should be specified to be suspended */ + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); + + /* Check if power down at system power domain level is requested */ + if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) { + /* Issue SCMI command for SYSTEM_SUSPEND */ + ret = scmi_sys_pwr_state_set(scmi_handle, + SCMI_SYS_PWR_FORCEFUL_REQ, + SCMI_SYS_PWR_SUSPEND); + if (ret != SCMI_E_SUCCESS) { + ERROR("SCMI system power domain suspend return 0x%x unexpected\n", + ret); + panic(); + } + return; + } +#if !HW_ASSISTED_COHERENCY + int lvl; + uint32_t scmi_pwr_state = 0; + /* + * If we reach here, then assert that power down at system power domain + * level is running. + */ + assert(css_system_pwr_state(target_state) == ARM_LOCAL_STATE_RUN); + + /* For level 0, specify `scmi_power_state_sleep` as the power state */ + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, ARM_PWR_LVL0, + scmi_power_state_sleep); + + for (lvl = ARM_PWR_LVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) { + if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN) + break; + + assert(target_state->pwr_domain_state[lvl] == + ARM_LOCAL_STATE_OFF); + /* + * Specify `scmi_power_state_off` as power state for higher + * levels. + */ + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, + scmi_power_state_off); + } + + SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); + + ret = scmi_pwr_state_set(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()], + scmi_pwr_state); + + if (ret != SCMI_E_SUCCESS) { + ERROR("SCMI set power state command return 0x%x unexpected\n", + ret); + panic(); + } +#endif +} + +/* + * Helper function to turn off a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_off(const struct psci_power_state *target_state) +{ + int lvl = 0, ret; + uint32_t scmi_pwr_state = 0; + + /* At-least the CPU level should be specified to be OFF */ + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); + + /* PSCI CPU OFF cannot be used to turn OFF system power domain */ + assert(css_system_pwr_state(target_state) == ARM_LOCAL_STATE_RUN); + + for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) { + if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN) + break; + + assert(target_state->pwr_domain_state[lvl] == + ARM_LOCAL_STATE_OFF); + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, + scmi_power_state_off); + } + + SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); + + ret = scmi_pwr_state_set(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()], + scmi_pwr_state); + + if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) { + ERROR("SCMI set power state command return 0x%x unexpected\n", + ret); + panic(); + } +} + +/* + * Helper function to turn ON a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_on(u_register_t mpidr) +{ + int lvl = 0, ret, core_pos; + uint32_t scmi_pwr_state = 0; + + for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) + SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, + scmi_power_state_on); + + SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); + + core_pos = plat_core_pos_by_mpidr(mpidr); + assert(core_pos >= 0 && core_pos < PLATFORM_CORE_COUNT); + + ret = scmi_pwr_state_set(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[core_pos], + scmi_pwr_state); + + if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) { + ERROR("SCMI set power state command return 0x%x unexpected\n", + ret); + panic(); + } +} + +/* + * Helper function to get the power state of a power domain node as reported + * by the SCP. + */ +int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level) +{ + int ret, cpu_idx; + uint32_t scmi_pwr_state = 0, lvl_state; + + /* We don't support get power state at the system power domain level */ + if ((power_level > PLAT_MAX_PWR_LVL) || + (power_level == CSS_SYSTEM_PWR_DMN_LVL)) { + WARN("Invalid power level %u specified for SCMI get power state\n", + power_level); + return PSCI_E_INVALID_PARAMS; + } + + cpu_idx = plat_core_pos_by_mpidr(mpidr); + assert(cpu_idx > -1); + + ret = scmi_pwr_state_get(scmi_handle, + plat_css_core_pos_to_scmi_dmn_id_map[cpu_idx], + &scmi_pwr_state); + + if (ret != SCMI_E_SUCCESS) { + WARN("SCMI get power state command return 0x%x unexpected\n", + ret); + return PSCI_E_INVALID_PARAMS; + } + + /* + * Find the maximum power level described in the get power state + * command. If it is less than the requested power level, then assume + * the requested power level is ON. + */ + if (SCMI_GET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state) < power_level) + return HW_ON; + + lvl_state = SCMI_GET_PWR_STATE_LVL(scmi_pwr_state, power_level); + if (lvl_state == scmi_power_state_on) + return HW_ON; + + assert((lvl_state == scmi_power_state_off) || + (lvl_state == scmi_power_state_sleep)); + return HW_OFF; +} + +void __dead2 css_scp_system_off(int state) +{ + int ret; + + /* + * Disable GIC CPU interface to prevent pending interrupt from waking + * up the AP from WFI. + */ + plat_arm_gic_cpuif_disable(); + + /* + * Issue SCMI command. First issue a graceful + * request and if that fails force the request. + */ + ret = scmi_sys_pwr_state_set(scmi_handle, + SCMI_SYS_PWR_FORCEFUL_REQ, + state); + + if (ret != SCMI_E_SUCCESS) { + ERROR("SCMI system power state set 0x%x returns unexpected 0x%x\n", + state, ret); + panic(); + } + wfi(); + ERROR("CSS set power state: operation not handled.\n"); + panic(); +} + +/* + * Helper function to shutdown the system via SCMI. + */ +void __dead2 css_scp_sys_shutdown(void) +{ + css_scp_system_off(SCMI_SYS_PWR_SHUTDOWN); +} + +/* + * Helper function to reset the system via SCMI. + */ +void __dead2 css_scp_sys_reboot(void) +{ + css_scp_system_off(SCMI_SYS_PWR_COLD_RESET); +} + +static int scmi_ap_core_init(scmi_channel_t *ch) +{ +#if PROGRAMMABLE_RESET_ADDRESS + uint32_t version; + int ret; + + ret = scmi_proto_version(ch, SCMI_AP_CORE_PROTO_ID, &version); + if (ret != SCMI_E_SUCCESS) { + WARN("SCMI AP core protocol version message failed\n"); + return -1; + } + + if (!is_scmi_version_compatible(SCMI_AP_CORE_PROTO_VER, version)) { + WARN("SCMI AP core protocol version 0x%x incompatible with driver version 0x%x\n", + version, SCMI_AP_CORE_PROTO_VER); + return -1; + } + INFO("SCMI AP core protocol version 0x%x detected\n", version); +#endif + return 0; +} + +void __init plat_arm_pwrc_setup(void) +{ + channel.info = plat_css_get_scmi_info(); + channel.lock = ARM_SCMI_LOCK_GET_INSTANCE; + scmi_handle = scmi_init(&channel); + if (scmi_handle == NULL) { + ERROR("SCMI Initialization failed\n"); + panic(); + } + if (scmi_ap_core_init(&channel) < 0) { + ERROR("SCMI AP core protocol initialization failed\n"); + panic(); + } +} + +/****************************************************************************** + * This function overrides the default definition for ARM platforms. Initialize + * the SCMI driver, query capability via SCMI and modify the PSCI capability + * based on that. + *****************************************************************************/ +const plat_psci_ops_t *css_scmi_override_pm_ops(plat_psci_ops_t *ops) +{ + uint32_t msg_attr; + int ret; + + assert(scmi_handle); + + /* Check that power domain POWER_STATE_SET message is supported */ + ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID, + SCMI_PWR_STATE_SET_MSG, &msg_attr); + if (ret != SCMI_E_SUCCESS) { + ERROR("Set power state command is not supported by SCMI\n"); + panic(); + } + + /* + * Don't support PSCI NODE_HW_STATE call if SCMI doesn't support + * POWER_STATE_GET message. + */ + ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID, + SCMI_PWR_STATE_GET_MSG, &msg_attr); + if (ret != SCMI_E_SUCCESS) + ops->get_node_hw_state = NULL; + + /* Check if the SCMI SYSTEM_POWER_STATE_SET message is supported */ + ret = scmi_proto_msg_attr(scmi_handle, SCMI_SYS_PWR_PROTO_ID, + SCMI_SYS_PWR_STATE_SET_MSG, &msg_attr); + if (ret != SCMI_E_SUCCESS) { + /* System power management operations are not supported */ + ops->system_off = NULL; + ops->system_reset = NULL; + ops->get_sys_suspend_power_state = NULL; + } else { + if (!(msg_attr & SCMI_SYS_PWR_SUSPEND_SUPPORTED)) { + /* + * System power management protocol is available, but + * it does not support SYSTEM SUSPEND. + */ + ops->get_sys_suspend_power_state = NULL; + } + if (!(msg_attr & SCMI_SYS_PWR_WARM_RESET_SUPPORTED)) { + /* + * WARM reset is not available. + */ + ops->system_reset2 = NULL; + } + } + + return ops; +} + +int css_system_reset2(int is_vendor, int reset_type, u_register_t cookie) +{ + if (is_vendor || (reset_type != PSCI_RESET2_SYSTEM_WARM_RESET)) + return PSCI_E_INVALID_PARAMS; + + css_scp_system_off(SCMI_SYS_PWR_WARM_RESET); + /* + * css_scp_system_off cannot return (it is a __dead function), + * but css_system_reset2 has to return some value, even in + * this case. + */ + return 0; +} + +#if PROGRAMMABLE_RESET_ADDRESS +void plat_arm_program_trusted_mailbox(uintptr_t address) +{ + int ret; + + assert(scmi_handle); + ret = scmi_ap_core_set_reset_addr(scmi_handle, address, + SCMI_AP_CORE_LOCK_ATTR); + if (ret != SCMI_E_SUCCESS) { + ERROR("CSS: Failed to program reset address: %d\n", ret); + panic(); + } +} +#endif diff --git a/drivers/arm/css/scp/css_pm_scpi.c b/drivers/arm/css/scp/css_pm_scpi.c new file mode 100644 index 0000000..b4019ce --- /dev/null +++ b/drivers/arm/css/scp/css_pm_scpi.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include +#include +#include +#include + +/* + * This file implements the SCP power management functions using SCPI protocol. + */ + +/* + * Helper function to inform power down state to SCP. + */ +void css_scp_suspend(const struct psci_power_state *target_state) +{ + uint32_t cluster_state = scpi_power_on; + uint32_t system_state = scpi_power_on; + + /* Check if power down at system power domain level is requested */ + if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) + system_state = scpi_power_retention; + + /* Cluster is to be turned off, so disable coherency */ + if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) + cluster_state = scpi_power_off; + + /* + * Ask the SCP to power down the appropriate components depending upon + * their state. + */ + scpi_set_css_power_state(read_mpidr_el1(), + scpi_power_off, + cluster_state, + system_state); +} + +/* + * Helper function to turn off a CPU power domain and its parent power domains + * if applicable. Since SCPI doesn't differentiate between OFF and suspend, we + * call the suspend helper here. + */ +void css_scp_off(const struct psci_power_state *target_state) +{ + css_scp_suspend(target_state); +} + +/* + * Helper function to turn ON a CPU power domain and its parent power domains + * if applicable. + */ +void css_scp_on(u_register_t mpidr) +{ + /* + * SCP takes care of powering up parent power domains so we + * only need to care about level 0 + */ + scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on, + scpi_power_on); +} + +/* + * Helper function to get the power state of a power domain node as reported + * by the SCP. + */ +int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level) +{ + int rc, element; + unsigned int cpu_state, cluster_state; + + /* + * The format of 'power_level' is implementation-defined, but 0 must + * mean a CPU. We also allow 1 to denote the cluster + */ + if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) + return PSCI_E_INVALID_PARAMS; + + /* Query SCP */ + rc = scpi_get_css_power_state(mpidr, &cpu_state, &cluster_state); + if (rc != 0) + return PSCI_E_INVALID_PARAMS; + + /* Map power states of CPU and cluster to expected PSCI return codes */ + if (power_level == ARM_PWR_LVL0) { + /* + * The CPU state returned by SCP is an 8-bit bit mask + * corresponding to each CPU in the cluster + */ +#if ARM_PLAT_MT + /* + * The current SCPI driver only caters for single-threaded + * platforms. Hence we ignore the thread ID (which is always 0) + * for such platforms. + */ + element = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; +#else + element = mpidr & MPIDR_AFFLVL_MASK; +#endif /* ARM_PLAT_MT */ + return CSS_CPU_PWR_STATE(cpu_state, element) == + CSS_CPU_PWR_STATE_ON ? HW_ON : HW_OFF; + } else { + assert(cluster_state == CSS_CLUSTER_PWR_STATE_ON || + cluster_state == CSS_CLUSTER_PWR_STATE_OFF); + return cluster_state == CSS_CLUSTER_PWR_STATE_ON ? HW_ON : + HW_OFF; + } +} + +/* + * Helper function to shutdown the system via SCPI. + */ +void __dead2 css_scp_sys_shutdown(void) +{ + uint32_t response; + + /* + * Disable GIC CPU interface to prevent pending interrupt + * from waking up the AP from WFI. + */ + plat_arm_gic_cpuif_disable(); + + /* Send the power down request to the SCP */ + response = scpi_sys_power_state(scpi_system_shutdown); + + if (response != SCP_OK) { + ERROR("CSS System Off: SCP error %u.\n", response); + panic(); + } + wfi(); + ERROR("CSS System Off: operation not handled.\n"); + panic(); +} + +/* + * Helper function to reset the system via SCPI. + */ +void __dead2 css_scp_sys_reboot(void) +{ + uint32_t response; + + /* + * Disable GIC CPU interface to prevent pending interrupt + * from waking up the AP from WFI. + */ + plat_arm_gic_cpuif_disable(); + + /* Send the system reset request to the SCP */ + response = scpi_sys_power_state(scpi_system_reboot); + + if (response != SCP_OK) { + ERROR("CSS System Reset: SCP error %u.\n", response); + panic(); + } + wfi(); + ERROR("CSS System Reset: operation not handled.\n"); + panic(); +} diff --git a/drivers/arm/css/scp/css_sds.c b/drivers/arm/css/scp/css_sds.c new file mode 100644 index 0000000..2bfe750 --- /dev/null +++ b/drivers/arm/css/scp/css_sds.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../sds/sds.h" + +int css_scp_boot_image_xfer(void *image, unsigned int image_size) +{ + int ret; + unsigned int image_offset, image_flags; + + ret = sds_init(); + if (ret != SDS_OK) { + ERROR("SCP SDS initialization failed\n"); + panic(); + } + + VERBOSE("Writing SCP image metadata\n"); + image_offset = (uintptr_t) image - ARM_TRUSTED_SRAM_BASE; + ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_ADDR_OFFSET, + &image_offset, SDS_SCP_IMG_ADDR_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret != SDS_OK) + goto sds_fail; + + ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_SIZE_OFFSET, + &image_size, SDS_SCP_IMG_SIZE_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret != SDS_OK) + goto sds_fail; + + VERBOSE("Marking SCP image metadata as valid\n"); + image_flags = SDS_SCP_IMG_VALID_FLAG_BIT; + ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_FLAG_OFFSET, + &image_flags, SDS_SCP_IMG_FLAG_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret != SDS_OK) + goto sds_fail; + + return 0; +sds_fail: + ERROR("SCP SDS write to SCP IMG struct failed\n"); + panic(); +} + +/* + * API to wait for SCP to signal till it's ready after booting the transferred + * image. + */ +int css_scp_boot_ready(void) +{ + uint32_t scp_feature_availability_flags; + int ret, retry = CSS_SCP_READY_10US_RETRIES; + + + VERBOSE("Waiting for SCP RAM to complete its initialization process\n"); + + /* Wait for the SCP RAM Firmware to complete its initialization process */ + while (retry > 0) { + ret = sds_struct_read(SDS_FEATURE_AVAIL_STRUCT_ID, 0, + &scp_feature_availability_flags, + SDS_FEATURE_AVAIL_SIZE, + SDS_ACCESS_MODE_NON_CACHED); + if (ret == SDS_ERR_STRUCT_NOT_FINALIZED) + continue; + + if (ret != SDS_OK) { + ERROR(" sds_struct_read failed\n"); + panic(); + } + + if (scp_feature_availability_flags & + SDS_FEATURE_AVAIL_SCP_RAM_READY_BIT) + return 0; + + udelay(10); + retry--; + } + + ERROR("Timeout of %d ms expired waiting for SCP RAM Ready flag\n", + CSS_SCP_READY_10US_RETRIES/100); + + plat_panic_handler(); +} diff --git a/include/drivers/arm/css/css_scp.h b/include/drivers/arm/css/css_scp.h new file mode 100644 index 0000000..f3c08c5 --- /dev/null +++ b/include/drivers/arm/css/css_scp.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CSS_SCP_H +#define CSS_SCP_H + +#include + +#include + +#include + +/* Forward declarations */ +struct psci_power_state; + +/* API for power management by SCP */ +int css_system_reset2(int is_vendor, int reset_type, u_register_t cookie); +void css_scp_suspend(const struct psci_power_state *target_state); +void css_scp_off(const struct psci_power_state *target_state); +void css_scp_on(u_register_t mpidr); +int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level); +void __dead2 css_scp_sys_shutdown(void); +void __dead2 css_scp_sys_reboot(void); +void __dead2 css_scp_system_off(int state); + +/* API for SCP Boot Image transfer. Return 0 on success, -1 on error */ +int css_scp_boot_image_xfer(void *image, unsigned int image_size); + +/* + * API to wait for SCP to signal till it's ready after booting the transferred + * image. + */ +int css_scp_boot_ready(void); + +#if CSS_LOAD_SCP_IMAGES + +/* + * All CSS platforms load SCP_BL2/SCP_BL2U just below BL2 (this is where BL31 + * usually resides except when ARM_BL31_IN_DRAM is + * set). Ensure that SCP_BL2/SCP_BL2U do not overflow into tb_fw_config. + */ +CASSERT(SCP_BL2_LIMIT <= BL2_BASE, assert_scp_bl2_overwrite_bl2); +CASSERT(SCP_BL2U_LIMIT <= BL2_BASE, assert_scp_bl2u_overwrite_bl2); + +CASSERT(SCP_BL2_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2_overflow); +CASSERT(SCP_BL2U_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2u_overflow); +#endif + +#endif /* CSS_SCP_H */ diff --git a/plat/arm/css/common/css_bl2_setup.c b/plat/arm/css/common/css_bl2_setup.c index 1538e29..002c6eb 100644 --- a/plat/arm/css/common/css_bl2_setup.c +++ b/plat/arm/css/common/css_bl2_setup.c @@ -8,13 +8,12 @@ #include #include +#include #include #include #include #include -#include "../drivers/scp/css_scp.h" - /* Weak definition may be overridden in specific CSS based platform */ #pragma weak plat_arm_bl2_handle_scp_bl2 diff --git a/plat/arm/css/common/css_bl2u_setup.c b/plat/arm/css/common/css_bl2u_setup.c index c3b4f2b..15cf4f6 100644 --- a/plat/arm/css/common/css_bl2u_setup.c +++ b/plat/arm/css/common/css_bl2u_setup.c @@ -6,11 +6,10 @@ #include #include +#include #include #include -#include "../drivers/scp/css_scp.h" - /* Weak definition may be overridden in specific CSS based platform */ #pragma weak bl2u_plat_handle_scp_bl2u diff --git a/plat/arm/css/common/css_common.mk b/plat/arm/css/common/css_common.mk index 36da732..fba9cdc 100644 --- a/plat/arm/css/common/css_common.mk +++ b/plat/arm/css/common/css_common.mk @@ -27,15 +27,15 @@ ifeq (${CSS_USE_SCMI_SDS_DRIVER},0) BL31_SOURCES += drivers/arm/css/mhu/css_mhu.c \ - drivers/arm/css/scpi/css_scpi.c \ - plat/arm/css/drivers/scp/css_pm_scpi.c + drivers/arm/css/scp/css_pm_scpi.c \ + drivers/arm/css/scpi/css_scpi.c else BL31_SOURCES += drivers/arm/css/mhu/css_mhu_doorbell.c \ drivers/arm/css/scmi/scmi_ap_core_proto.c \ drivers/arm/css/scmi/scmi_common.c \ drivers/arm/css/scmi/scmi_pwr_dmn_proto.c \ drivers/arm/css/scmi/scmi_sys_pwr_proto.c \ - plat/arm/css/drivers/scp/css_pm_scmi.c + drivers/arm/css/scp/css_pm_scmi.c endif # Process CSS_LOAD_SCP_IMAGES flag @@ -49,19 +49,19 @@ endif ifeq (${CSS_USE_SCMI_SDS_DRIVER},1) - BL2U_SOURCES += plat/arm/css/drivers/scp/css_sds.c \ + BL2U_SOURCES += drivers/arm/css/scp/css_sds.c \ plat/arm/css/drivers/sds/sds.c - BL2_SOURCES += plat/arm/css/drivers/scp/css_sds.c \ + BL2_SOURCES += drivers/arm/css/scp/css_sds.c \ plat/arm/css/drivers/sds/sds.c else - BL2U_SOURCES += drivers/arm/css/mhu/css_mhu.c \ - drivers/arm/css/scpi/css_scpi.c \ - plat/arm/css/drivers/scp/css_bom_bootloader.c + BL2U_SOURCES += drivers/arm/css/mhu/css_mhu.c \ + drivers/arm/css/scp/css_bom_bootloader.c \ + drivers/arm/css/scpi/css_scpi.c - BL2_SOURCES += drivers/arm/css/mhu/css_mhu.c \ - drivers/arm/css/scpi/css_scpi.c \ - plat/arm/css/drivers/scp/css_bom_bootloader.c + BL2_SOURCES += drivers/arm/css/mhu/css_mhu.c \ + drivers/arm/css/scp/css_bom_bootloader.c \ + drivers/arm/css/scpi/css_scpi.c # Enable option to detect whether the SCP ROM firmware in use predates version # 1.7.0 and therefore, is incompatible. CSS_DETECT_PRE_1_7_0_SCP := 1 diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 47412df..f6fc6aa 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -11,13 +11,12 @@ #include #include +#include #include #include #include #include -#include "../drivers/scp/css_scp.h" - /* Allow CSS platforms to override `plat_arm_psci_pm_ops` */ #pragma weak plat_arm_psci_pm_ops diff --git a/plat/arm/css/common/sp_min/css_sp_min.mk b/plat/arm/css/common/sp_min/css_sp_min.mk index a7c61be..6523a16 100644 --- a/plat/arm/css/common/sp_min/css_sp_min.mk +++ b/plat/arm/css/common/sp_min/css_sp_min.mk @@ -10,12 +10,12 @@ ifeq (${CSS_USE_SCMI_SDS_DRIVER},0) BL32_SOURCES += drivers/arm/css/mhu/css_mhu.c \ - drivers/arm/css/scpi/css_scpi.c \ - plat/arm/css/drivers/scp/css_pm_scpi.c + drivers/arm/css/scp/css_pm_scpi.c \ + drivers/arm/css/scpi/css_scpi.c else BL32_SOURCES += drivers/arm/css/mhu/css_mhu_doorbell.c \ + drivers/arm/css/scp/css_pm_scmi.c \ drivers/arm/css/scmi/scmi_common.c \ drivers/arm/css/scmi/scmi_pwr_dmn_proto.c \ - drivers/arm/css/scmi/scmi_sys_pwr_proto.c \ - plat/arm/css/drivers/scp/css_pm_scmi.c + drivers/arm/css/scmi/scmi_sys_pwr_proto.c endif diff --git a/plat/arm/css/drivers/scp/css_bom_bootloader.c b/plat/arm/css/drivers/scp/css_bom_bootloader.c deleted file mode 100644 index 40880da..0000000 --- a/plat/arm/css/drivers/scp/css_bom_bootloader.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "css_scp.h" - -/* ID of the MHU slot used for the BOM protocol */ -#define BOM_MHU_SLOT_ID 0 - -/* Boot commands sent from AP -> SCP */ -#define BOOT_CMD_INFO 0x00 -#define BOOT_CMD_DATA 0x01 - -/* BOM command header */ -typedef struct { - uint32_t id : 8; - uint32_t reserved : 24; -} bom_cmd_t; - -typedef struct { - uint32_t image_size; - uint32_t checksum; -} cmd_info_payload_t; - -/* - * Unlike the SCPI protocol, the boot protocol uses the same memory region - * for both AP -> SCP and SCP -> AP transfers; define the address of this... - */ -#define BOM_SHARED_MEM PLAT_CSS_SCP_COM_SHARED_MEM_BASE -#define BOM_CMD_HEADER ((bom_cmd_t *) BOM_SHARED_MEM) -#define BOM_CMD_PAYLOAD ((void *) (BOM_SHARED_MEM + sizeof(bom_cmd_t))) - -typedef struct { - /* Offset from the base address of the Trusted RAM */ - uint32_t offset; - uint32_t block_size; -} cmd_data_payload_t; - -/* - * All CSS platforms load SCP_BL2/SCP_BL2U just below BL2 (this is where BL31 - * usually resides except when ARM_BL31_IN_DRAM is - * set). Ensure that SCP_BL2/SCP_BL2U do not overflow into shared RAM and - * the tb_fw_config. - */ -CASSERT(SCP_BL2_LIMIT <= BL2_BASE, assert_scp_bl2_overwrite_bl2); -CASSERT(SCP_BL2U_LIMIT <= BL2_BASE, assert_scp_bl2u_overwrite_bl2); - -CASSERT(SCP_BL2_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2_overflow); -CASSERT(SCP_BL2U_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2u_overflow); - -static void scp_boot_message_start(void) -{ - mhu_secure_message_start(BOM_MHU_SLOT_ID); -} - -static void scp_boot_message_send(size_t payload_size) -{ - /* Ensure that any write to the BOM payload area is seen by SCP before - * we write to the MHU register. If these 2 writes were reordered by - * the CPU then SCP would read stale payload data */ - dmbst(); - - /* Send command to SCP */ - mhu_secure_message_send(BOM_MHU_SLOT_ID); -} - -static uint32_t scp_boot_message_wait(size_t size) -{ - uint32_t mhu_status; - - mhu_status = mhu_secure_message_wait(); - - /* Expect an SCP Boot Protocol message, reject any other protocol */ - if (mhu_status != (1 << BOM_MHU_SLOT_ID)) { - ERROR("MHU: Unexpected protocol (MHU status: 0x%x)\n", - mhu_status); - panic(); - } - - /* Ensure that any read to the BOM payload area is done after reading - * the MHU register. If these 2 reads were reordered then the CPU would - * read invalid payload data */ - dmbld(); - - return *(uint32_t *) BOM_SHARED_MEM; -} - -static void scp_boot_message_end(void) -{ - mhu_secure_message_end(BOM_MHU_SLOT_ID); -} - -int css_scp_boot_image_xfer(void *image, unsigned int image_size) -{ - uint32_t response; - uint32_t checksum; - cmd_info_payload_t *cmd_info_payload; - cmd_data_payload_t *cmd_data_payload; - - assert((uintptr_t) image == SCP_BL2_BASE); - - if ((image_size == 0) || (image_size % 4 != 0)) { - ERROR("Invalid size for the SCP_BL2 image. Must be a multiple of " - "4 bytes and not zero (current size = 0x%x)\n", - image_size); - return -1; - } - - /* Extract the checksum from the image */ - checksum = *(uint32_t *) image; - image = (char *) image + sizeof(checksum); - image_size -= sizeof(checksum); - - mhu_secure_init(); - - VERBOSE("Send info about the SCP_BL2 image to be transferred to SCP\n"); - - /* - * Send information about the SCP firmware image about to be transferred - * to SCP - */ - scp_boot_message_start(); - - BOM_CMD_HEADER->id = BOOT_CMD_INFO; - cmd_info_payload = BOM_CMD_PAYLOAD; - cmd_info_payload->image_size = image_size; - cmd_info_payload->checksum = checksum; - - scp_boot_message_send(sizeof(*cmd_info_payload)); -#if CSS_DETECT_PRE_1_7_0_SCP - { - const uint32_t deprecated_scp_nack_cmd = 0x404; - uint32_t mhu_status; - - VERBOSE("Detecting SCP version incompatibility\n"); - - mhu_status = mhu_secure_message_wait(); - if (mhu_status == deprecated_scp_nack_cmd) { - ERROR("Detected an incompatible version of the SCP firmware.\n"); - ERROR("Only versions from v1.7.0 onwards are supported.\n"); - ERROR("Please update the SCP firmware.\n"); - return -1; - } - - VERBOSE("SCP version looks OK\n"); - } -#endif /* CSS_DETECT_PRE_1_7_0_SCP */ - response = scp_boot_message_wait(sizeof(response)); - scp_boot_message_end(); - - if (response != 0) { - ERROR("SCP BOOT_CMD_INFO returned error %u\n", response); - return -1; - } - - VERBOSE("Transferring SCP_BL2 image to SCP\n"); - - /* Transfer SCP_BL2 image to SCP */ - scp_boot_message_start(); - - BOM_CMD_HEADER->id = BOOT_CMD_DATA; - cmd_data_payload = BOM_CMD_PAYLOAD; - cmd_data_payload->offset = (uintptr_t) image - ARM_TRUSTED_SRAM_BASE; - cmd_data_payload->block_size = image_size; - - scp_boot_message_send(sizeof(*cmd_data_payload)); - response = scp_boot_message_wait(sizeof(response)); - scp_boot_message_end(); - - if (response != 0) { - ERROR("SCP BOOT_CMD_DATA returned error %u\n", response); - return -1; - } - - return 0; -} - -int css_scp_boot_ready(void) -{ - VERBOSE("Waiting for SCP to signal it is ready to go on\n"); - - /* Wait for SCP to signal it's ready */ - return scpi_wait_ready(); -} diff --git a/plat/arm/css/drivers/scp/css_pm_scmi.c b/plat/arm/css/drivers/scp/css_pm_scmi.c deleted file mode 100644 index 2980d9a..0000000 --- a/plat/arm/css/drivers/scp/css_pm_scmi.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "css_scp.h" - -/* - * This file implements the SCP helper functions using SCMI protocol. - */ - -/* - * SCMI power state parameter bit field encoding for ARM CSS platforms. - * - * 31 20 19 16 15 12 11 8 7 4 3 0 - * +-------------------------------------------------------------+ - * | SBZ | Max level | Level 3 | Level 2 | Level 1 | Level 0 | - * | | | state | state | state | state | - * +-------------------------------------------------------------+ - * - * `Max level` encodes the highest level that has a valid power state - * encoded in the power state. - */ -#define SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT 16 -#define SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH 4 -#define SCMI_PWR_STATE_MAX_PWR_LVL_MASK \ - ((1 << SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH) - 1) -#define SCMI_SET_PWR_STATE_MAX_PWR_LVL(_power_state, _max_level) \ - (_power_state) |= ((_max_level) & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)\ - << SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT -#define SCMI_GET_PWR_STATE_MAX_PWR_LVL(_power_state) \ - (((_power_state) >> SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT) \ - & SCMI_PWR_STATE_MAX_PWR_LVL_MASK) - -#define SCMI_PWR_STATE_LVL_WIDTH 4 -#define SCMI_PWR_STATE_LVL_MASK \ - ((1 << SCMI_PWR_STATE_LVL_WIDTH) - 1) -#define SCMI_SET_PWR_STATE_LVL(_power_state, _level, _level_state) \ - (_power_state) |= ((_level_state) & SCMI_PWR_STATE_LVL_MASK) \ - << (SCMI_PWR_STATE_LVL_WIDTH * (_level)) -#define SCMI_GET_PWR_STATE_LVL(_power_state, _level) \ - (((_power_state) >> (SCMI_PWR_STATE_LVL_WIDTH * (_level))) & \ - SCMI_PWR_STATE_LVL_MASK) - -/* - * The SCMI power state enumeration for a power domain level - */ -typedef enum { - scmi_power_state_off = 0, - scmi_power_state_on = 1, - scmi_power_state_sleep = 2, -} scmi_power_state_t; - -/* - * The global handle for invoking the SCMI driver APIs after the driver - * has been initialized. - */ -static void *scmi_handle; - -/* The SCMI channel global object */ -static scmi_channel_t channel; - -ARM_SCMI_INSTANTIATE_LOCK; - -/* - * Helper function to suspend a CPU power domain and its parent power domains - * if applicable. - */ -void css_scp_suspend(const struct psci_power_state *target_state) -{ - int ret; - - /* At least power domain level 0 should be specified to be suspended */ - assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_OFF); - - /* Check if power down at system power domain level is requested */ - if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) { - /* Issue SCMI command for SYSTEM_SUSPEND */ - ret = scmi_sys_pwr_state_set(scmi_handle, - SCMI_SYS_PWR_FORCEFUL_REQ, - SCMI_SYS_PWR_SUSPEND); - if (ret != SCMI_E_SUCCESS) { - ERROR("SCMI system power domain suspend return 0x%x unexpected\n", - ret); - panic(); - } - return; - } -#if !HW_ASSISTED_COHERENCY - int lvl; - uint32_t scmi_pwr_state = 0; - /* - * If we reach here, then assert that power down at system power domain - * level is running. - */ - assert(css_system_pwr_state(target_state) == ARM_LOCAL_STATE_RUN); - - /* For level 0, specify `scmi_power_state_sleep` as the power state */ - SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, ARM_PWR_LVL0, - scmi_power_state_sleep); - - for (lvl = ARM_PWR_LVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) { - if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN) - break; - - assert(target_state->pwr_domain_state[lvl] == - ARM_LOCAL_STATE_OFF); - /* - * Specify `scmi_power_state_off` as power state for higher - * levels. - */ - SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, - scmi_power_state_off); - } - - SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); - - ret = scmi_pwr_state_set(scmi_handle, - plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()], - scmi_pwr_state); - - if (ret != SCMI_E_SUCCESS) { - ERROR("SCMI set power state command return 0x%x unexpected\n", - ret); - panic(); - } -#endif -} - -/* - * Helper function to turn off a CPU power domain and its parent power domains - * if applicable. - */ -void css_scp_off(const struct psci_power_state *target_state) -{ - int lvl = 0, ret; - uint32_t scmi_pwr_state = 0; - - /* At-least the CPU level should be specified to be OFF */ - assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_OFF); - - /* PSCI CPU OFF cannot be used to turn OFF system power domain */ - assert(css_system_pwr_state(target_state) == ARM_LOCAL_STATE_RUN); - - for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) { - if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN) - break; - - assert(target_state->pwr_domain_state[lvl] == - ARM_LOCAL_STATE_OFF); - SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, - scmi_power_state_off); - } - - SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); - - ret = scmi_pwr_state_set(scmi_handle, - plat_css_core_pos_to_scmi_dmn_id_map[plat_my_core_pos()], - scmi_pwr_state); - - if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) { - ERROR("SCMI set power state command return 0x%x unexpected\n", - ret); - panic(); - } -} - -/* - * Helper function to turn ON a CPU power domain and its parent power domains - * if applicable. - */ -void css_scp_on(u_register_t mpidr) -{ - int lvl = 0, ret, core_pos; - uint32_t scmi_pwr_state = 0; - - for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) - SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl, - scmi_power_state_on); - - SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1); - - core_pos = plat_core_pos_by_mpidr(mpidr); - assert(core_pos >= 0 && core_pos < PLATFORM_CORE_COUNT); - - ret = scmi_pwr_state_set(scmi_handle, - plat_css_core_pos_to_scmi_dmn_id_map[core_pos], - scmi_pwr_state); - - if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) { - ERROR("SCMI set power state command return 0x%x unexpected\n", - ret); - panic(); - } -} - -/* - * Helper function to get the power state of a power domain node as reported - * by the SCP. - */ -int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level) -{ - int ret, cpu_idx; - uint32_t scmi_pwr_state = 0, lvl_state; - - /* We don't support get power state at the system power domain level */ - if ((power_level > PLAT_MAX_PWR_LVL) || - (power_level == CSS_SYSTEM_PWR_DMN_LVL)) { - WARN("Invalid power level %u specified for SCMI get power state\n", - power_level); - return PSCI_E_INVALID_PARAMS; - } - - cpu_idx = plat_core_pos_by_mpidr(mpidr); - assert(cpu_idx > -1); - - ret = scmi_pwr_state_get(scmi_handle, - plat_css_core_pos_to_scmi_dmn_id_map[cpu_idx], - &scmi_pwr_state); - - if (ret != SCMI_E_SUCCESS) { - WARN("SCMI get power state command return 0x%x unexpected\n", - ret); - return PSCI_E_INVALID_PARAMS; - } - - /* - * Find the maximum power level described in the get power state - * command. If it is less than the requested power level, then assume - * the requested power level is ON. - */ - if (SCMI_GET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state) < power_level) - return HW_ON; - - lvl_state = SCMI_GET_PWR_STATE_LVL(scmi_pwr_state, power_level); - if (lvl_state == scmi_power_state_on) - return HW_ON; - - assert((lvl_state == scmi_power_state_off) || - (lvl_state == scmi_power_state_sleep)); - return HW_OFF; -} - -void __dead2 css_scp_system_off(int state) -{ - int ret; - - /* - * Disable GIC CPU interface to prevent pending interrupt from waking - * up the AP from WFI. - */ - plat_arm_gic_cpuif_disable(); - - /* - * Issue SCMI command. First issue a graceful - * request and if that fails force the request. - */ - ret = scmi_sys_pwr_state_set(scmi_handle, - SCMI_SYS_PWR_FORCEFUL_REQ, - state); - - if (ret != SCMI_E_SUCCESS) { - ERROR("SCMI system power state set 0x%x returns unexpected 0x%x\n", - state, ret); - panic(); - } - wfi(); - ERROR("CSS set power state: operation not handled.\n"); - panic(); -} - -/* - * Helper function to shutdown the system via SCMI. - */ -void __dead2 css_scp_sys_shutdown(void) -{ - css_scp_system_off(SCMI_SYS_PWR_SHUTDOWN); -} - -/* - * Helper function to reset the system via SCMI. - */ -void __dead2 css_scp_sys_reboot(void) -{ - css_scp_system_off(SCMI_SYS_PWR_COLD_RESET); -} - -static int scmi_ap_core_init(scmi_channel_t *ch) -{ -#if PROGRAMMABLE_RESET_ADDRESS - uint32_t version; - int ret; - - ret = scmi_proto_version(ch, SCMI_AP_CORE_PROTO_ID, &version); - if (ret != SCMI_E_SUCCESS) { - WARN("SCMI AP core protocol version message failed\n"); - return -1; - } - - if (!is_scmi_version_compatible(SCMI_AP_CORE_PROTO_VER, version)) { - WARN("SCMI AP core protocol version 0x%x incompatible with driver version 0x%x\n", - version, SCMI_AP_CORE_PROTO_VER); - return -1; - } - INFO("SCMI AP core protocol version 0x%x detected\n", version); -#endif - return 0; -} - -void __init plat_arm_pwrc_setup(void) -{ - channel.info = plat_css_get_scmi_info(); - channel.lock = ARM_SCMI_LOCK_GET_INSTANCE; - scmi_handle = scmi_init(&channel); - if (scmi_handle == NULL) { - ERROR("SCMI Initialization failed\n"); - panic(); - } - if (scmi_ap_core_init(&channel) < 0) { - ERROR("SCMI AP core protocol initialization failed\n"); - panic(); - } -} - -/****************************************************************************** - * This function overrides the default definition for ARM platforms. Initialize - * the SCMI driver, query capability via SCMI and modify the PSCI capability - * based on that. - *****************************************************************************/ -const plat_psci_ops_t *css_scmi_override_pm_ops(plat_psci_ops_t *ops) -{ - uint32_t msg_attr; - int ret; - - assert(scmi_handle); - - /* Check that power domain POWER_STATE_SET message is supported */ - ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID, - SCMI_PWR_STATE_SET_MSG, &msg_attr); - if (ret != SCMI_E_SUCCESS) { - ERROR("Set power state command is not supported by SCMI\n"); - panic(); - } - - /* - * Don't support PSCI NODE_HW_STATE call if SCMI doesn't support - * POWER_STATE_GET message. - */ - ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID, - SCMI_PWR_STATE_GET_MSG, &msg_attr); - if (ret != SCMI_E_SUCCESS) - ops->get_node_hw_state = NULL; - - /* Check if the SCMI SYSTEM_POWER_STATE_SET message is supported */ - ret = scmi_proto_msg_attr(scmi_handle, SCMI_SYS_PWR_PROTO_ID, - SCMI_SYS_PWR_STATE_SET_MSG, &msg_attr); - if (ret != SCMI_E_SUCCESS) { - /* System power management operations are not supported */ - ops->system_off = NULL; - ops->system_reset = NULL; - ops->get_sys_suspend_power_state = NULL; - } else { - if (!(msg_attr & SCMI_SYS_PWR_SUSPEND_SUPPORTED)) { - /* - * System power management protocol is available, but - * it does not support SYSTEM SUSPEND. - */ - ops->get_sys_suspend_power_state = NULL; - } - if (!(msg_attr & SCMI_SYS_PWR_WARM_RESET_SUPPORTED)) { - /* - * WARM reset is not available. - */ - ops->system_reset2 = NULL; - } - } - - return ops; -} - -int css_system_reset2(int is_vendor, int reset_type, u_register_t cookie) -{ - if (is_vendor || (reset_type != PSCI_RESET2_SYSTEM_WARM_RESET)) - return PSCI_E_INVALID_PARAMS; - - css_scp_system_off(SCMI_SYS_PWR_WARM_RESET); - /* - * css_scp_system_off cannot return (it is a __dead function), - * but css_system_reset2 has to return some value, even in - * this case. - */ - return 0; -} - -#if PROGRAMMABLE_RESET_ADDRESS -void plat_arm_program_trusted_mailbox(uintptr_t address) -{ - int ret; - - assert(scmi_handle); - ret = scmi_ap_core_set_reset_addr(scmi_handle, address, - SCMI_AP_CORE_LOCK_ATTR); - if (ret != SCMI_E_SUCCESS) { - ERROR("CSS: Failed to program reset address: %d\n", ret); - panic(); - } -} -#endif diff --git a/plat/arm/css/drivers/scp/css_pm_scpi.c b/plat/arm/css/drivers/scp/css_pm_scpi.c deleted file mode 100644 index 7e22816..0000000 --- a/plat/arm/css/drivers/scp/css_pm_scpi.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include - -#include -#include -#include -#include -#include - -#include "css_scp.h" - -/* - * This file implements the SCP power management functions using SCPI protocol. - */ - -/* - * Helper function to inform power down state to SCP. - */ -void css_scp_suspend(const struct psci_power_state *target_state) -{ - uint32_t cluster_state = scpi_power_on; - uint32_t system_state = scpi_power_on; - - /* Check if power down at system power domain level is requested */ - if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) - system_state = scpi_power_retention; - - /* Cluster is to be turned off, so disable coherency */ - if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) - cluster_state = scpi_power_off; - - /* - * Ask the SCP to power down the appropriate components depending upon - * their state. - */ - scpi_set_css_power_state(read_mpidr_el1(), - scpi_power_off, - cluster_state, - system_state); -} - -/* - * Helper function to turn off a CPU power domain and its parent power domains - * if applicable. Since SCPI doesn't differentiate between OFF and suspend, we - * call the suspend helper here. - */ -void css_scp_off(const struct psci_power_state *target_state) -{ - css_scp_suspend(target_state); -} - -/* - * Helper function to turn ON a CPU power domain and its parent power domains - * if applicable. - */ -void css_scp_on(u_register_t mpidr) -{ - /* - * SCP takes care of powering up parent power domains so we - * only need to care about level 0 - */ - scpi_set_css_power_state(mpidr, scpi_power_on, scpi_power_on, - scpi_power_on); -} - -/* - * Helper function to get the power state of a power domain node as reported - * by the SCP. - */ -int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level) -{ - int rc, element; - unsigned int cpu_state, cluster_state; - - /* - * The format of 'power_level' is implementation-defined, but 0 must - * mean a CPU. We also allow 1 to denote the cluster - */ - if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) - return PSCI_E_INVALID_PARAMS; - - /* Query SCP */ - rc = scpi_get_css_power_state(mpidr, &cpu_state, &cluster_state); - if (rc != 0) - return PSCI_E_INVALID_PARAMS; - - /* Map power states of CPU and cluster to expected PSCI return codes */ - if (power_level == ARM_PWR_LVL0) { - /* - * The CPU state returned by SCP is an 8-bit bit mask - * corresponding to each CPU in the cluster - */ -#if ARM_PLAT_MT - /* - * The current SCPI driver only caters for single-threaded - * platforms. Hence we ignore the thread ID (which is always 0) - * for such platforms. - */ - element = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; -#else - element = mpidr & MPIDR_AFFLVL_MASK; -#endif /* ARM_PLAT_MT */ - return CSS_CPU_PWR_STATE(cpu_state, element) == - CSS_CPU_PWR_STATE_ON ? HW_ON : HW_OFF; - } else { - assert(cluster_state == CSS_CLUSTER_PWR_STATE_ON || - cluster_state == CSS_CLUSTER_PWR_STATE_OFF); - return cluster_state == CSS_CLUSTER_PWR_STATE_ON ? HW_ON : - HW_OFF; - } -} - -/* - * Helper function to shutdown the system via SCPI. - */ -void __dead2 css_scp_sys_shutdown(void) -{ - uint32_t response; - - /* - * Disable GIC CPU interface to prevent pending interrupt - * from waking up the AP from WFI. - */ - plat_arm_gic_cpuif_disable(); - - /* Send the power down request to the SCP */ - response = scpi_sys_power_state(scpi_system_shutdown); - - if (response != SCP_OK) { - ERROR("CSS System Off: SCP error %u.\n", response); - panic(); - } - wfi(); - ERROR("CSS System Off: operation not handled.\n"); - panic(); -} - -/* - * Helper function to reset the system via SCPI. - */ -void __dead2 css_scp_sys_reboot(void) -{ - uint32_t response; - - /* - * Disable GIC CPU interface to prevent pending interrupt - * from waking up the AP from WFI. - */ - plat_arm_gic_cpuif_disable(); - - /* Send the system reset request to the SCP */ - response = scpi_sys_power_state(scpi_system_reboot); - - if (response != SCP_OK) { - ERROR("CSS System Reset: SCP error %u.\n", response); - panic(); - } - wfi(); - ERROR("CSS System Reset: operation not handled.\n"); - panic(); -} diff --git a/plat/arm/css/drivers/scp/css_scp.h b/plat/arm/css/drivers/scp/css_scp.h deleted file mode 100644 index f3c08c5..0000000 --- a/plat/arm/css/drivers/scp/css_scp.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef CSS_SCP_H -#define CSS_SCP_H - -#include - -#include - -#include - -/* Forward declarations */ -struct psci_power_state; - -/* API for power management by SCP */ -int css_system_reset2(int is_vendor, int reset_type, u_register_t cookie); -void css_scp_suspend(const struct psci_power_state *target_state); -void css_scp_off(const struct psci_power_state *target_state); -void css_scp_on(u_register_t mpidr); -int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level); -void __dead2 css_scp_sys_shutdown(void); -void __dead2 css_scp_sys_reboot(void); -void __dead2 css_scp_system_off(int state); - -/* API for SCP Boot Image transfer. Return 0 on success, -1 on error */ -int css_scp_boot_image_xfer(void *image, unsigned int image_size); - -/* - * API to wait for SCP to signal till it's ready after booting the transferred - * image. - */ -int css_scp_boot_ready(void); - -#if CSS_LOAD_SCP_IMAGES - -/* - * All CSS platforms load SCP_BL2/SCP_BL2U just below BL2 (this is where BL31 - * usually resides except when ARM_BL31_IN_DRAM is - * set). Ensure that SCP_BL2/SCP_BL2U do not overflow into tb_fw_config. - */ -CASSERT(SCP_BL2_LIMIT <= BL2_BASE, assert_scp_bl2_overwrite_bl2); -CASSERT(SCP_BL2U_LIMIT <= BL2_BASE, assert_scp_bl2u_overwrite_bl2); - -CASSERT(SCP_BL2_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2_overflow); -CASSERT(SCP_BL2U_BASE >= ARM_TB_FW_CONFIG_LIMIT, assert_scp_bl2u_overflow); -#endif - -#endif /* CSS_SCP_H */ diff --git a/plat/arm/css/drivers/scp/css_sds.c b/plat/arm/css/drivers/scp/css_sds.c deleted file mode 100644 index e3f6102..0000000 --- a/plat/arm/css/drivers/scp/css_sds.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include "css_scp.h" -#include "../sds/sds.h" - -int css_scp_boot_image_xfer(void *image, unsigned int image_size) -{ - int ret; - unsigned int image_offset, image_flags; - - ret = sds_init(); - if (ret != SDS_OK) { - ERROR("SCP SDS initialization failed\n"); - panic(); - } - - VERBOSE("Writing SCP image metadata\n"); - image_offset = (uintptr_t) image - ARM_TRUSTED_SRAM_BASE; - ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_ADDR_OFFSET, - &image_offset, SDS_SCP_IMG_ADDR_SIZE, - SDS_ACCESS_MODE_NON_CACHED); - if (ret != SDS_OK) - goto sds_fail; - - ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_SIZE_OFFSET, - &image_size, SDS_SCP_IMG_SIZE_SIZE, - SDS_ACCESS_MODE_NON_CACHED); - if (ret != SDS_OK) - goto sds_fail; - - VERBOSE("Marking SCP image metadata as valid\n"); - image_flags = SDS_SCP_IMG_VALID_FLAG_BIT; - ret = sds_struct_write(SDS_SCP_IMG_STRUCT_ID, SDS_SCP_IMG_FLAG_OFFSET, - &image_flags, SDS_SCP_IMG_FLAG_SIZE, - SDS_ACCESS_MODE_NON_CACHED); - if (ret != SDS_OK) - goto sds_fail; - - return 0; -sds_fail: - ERROR("SCP SDS write to SCP IMG struct failed\n"); - panic(); -} - -/* - * API to wait for SCP to signal till it's ready after booting the transferred - * image. - */ -int css_scp_boot_ready(void) -{ - uint32_t scp_feature_availability_flags; - int ret, retry = CSS_SCP_READY_10US_RETRIES; - - - VERBOSE("Waiting for SCP RAM to complete its initialization process\n"); - - /* Wait for the SCP RAM Firmware to complete its initialization process */ - while (retry > 0) { - ret = sds_struct_read(SDS_FEATURE_AVAIL_STRUCT_ID, 0, - &scp_feature_availability_flags, - SDS_FEATURE_AVAIL_SIZE, - SDS_ACCESS_MODE_NON_CACHED); - if (ret == SDS_ERR_STRUCT_NOT_FINALIZED) - continue; - - if (ret != SDS_OK) { - ERROR(" sds_struct_read failed\n"); - panic(); - } - - if (scp_feature_availability_flags & - SDS_FEATURE_AVAIL_SCP_RAM_READY_BIT) - return 0; - - udelay(10); - retry--; - } - - ERROR("Timeout of %d ms expired waiting for SCP RAM Ready flag\n", - CSS_SCP_READY_10US_RETRIES/100); - - plat_panic_handler(); -}