--- /dev/null
+/*
+ * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/css/css_mhu.h>
+#include <drivers/arm/css/css_scp.h>
+#include <drivers/arm/css/css_scpi.h>
+#include <plat/common/platform.h>
+#include <platform_def.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();
+}
--- /dev/null
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/css/css_scp.h>
+#include <drivers/arm/css/scmi.h>
+#include <plat/arm/common/plat_arm.h>
+#include <plat/arm/css/common/css_pm.h>
+#include <plat/common/platform.h>
+#include <platform_def.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
--- /dev/null
+/*
+ * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/css/css_scp.h>
+#include <drivers/arm/css/css_scpi.h>
+#include <plat/arm/common/plat_arm.h>
+#include <plat/arm/css/common/css_pm.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();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/arm/css/css_scp.h>
+#include <drivers/delay_timer.h>
+#include <plat/common/platform.h>
+#include <platform_def.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();
+}
--- /dev/null
+/*
+ * 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 <stdint.h>
+
+#include <platform_def.h>
+
+#include <lib/cassert.h>
+
+/* 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 */
#include <common/bl_common.h>
#include <common/debug.h>
+#include <drivers/arm/css/css_scp.h>
#include <lib/mmio.h>
#include <lib/utils.h>
#include <plat/arm/common/plat_arm.h>
#include <platform_def.h>
-#include "../drivers/scp/css_scp.h"
-
/* Weak definition may be overridden in specific CSS based platform */
#pragma weak plat_arm_bl2_handle_scp_bl2
#include <common/bl_common.h>
#include <common/debug.h>
+#include <drivers/arm/css/css_scp.h>
#include <plat/arm/common/plat_arm.h>
#include <plat/common/platform.h>
-#include "../drivers/scp/css_scp.h"
-
/* Weak definition may be overridden in specific CSS based platform */
#pragma weak bl2u_plat_handle_scp_bl2u
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
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
#include <arch_helpers.h>
#include <common/debug.h>
+#include <drivers/arm/css/css_scp.h>
#include <lib/cassert.h>
#include <plat/arm/common/plat_arm.h>
#include <plat/arm/css/common/css_pm.h>
#include <plat/common/platform.h>
-#include "../drivers/scp/css_scp.h"
-
/* Allow CSS platforms to override `plat_arm_psci_pm_ops` */
#pragma weak plat_arm_psci_pm_ops
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
+++ /dev/null
-/*
- * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <assert.h>
-#include <stdint.h>
-
-#include <arch_helpers.h>
-#include <common/debug.h>
-#include <drivers/arm/css/css_mhu.h>
-#include <drivers/arm/css/css_scpi.h>
-#include <plat/common/platform.h>
-#include <platform_def.h>
-
-#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();
-}
+++ /dev/null
-/*
- * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <assert.h>
-#include <string.h>
-
-#include <arch_helpers.h>
-#include <common/debug.h>
-#include <drivers/arm/css/scmi.h>
-#include <plat/arm/common/plat_arm.h>
-#include <plat/arm/css/common/css_pm.h>
-#include <plat/common/platform.h>
-#include <platform_def.h>
-
-#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
+++ /dev/null
-/*
- * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <assert.h>
-
-#include <arch_helpers.h>
-#include <common/debug.h>
-#include <drivers/arm/css/css_scpi.h>
-#include <plat/arm/common/plat_arm.h>
-#include <plat/arm/css/common/css_pm.h>
-
-#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();
-}
+++ /dev/null
-/*
- * 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 <stdint.h>
-
-#include <platform_def.h>
-
-#include <lib/cassert.h>
-
-/* 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 */
+++ /dev/null
-/*
- * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <assert.h>
-#include <stdint.h>
-
-#include <arch_helpers.h>
-#include <common/debug.h>
-#include <drivers/delay_timer.h>
-#include <plat/common/platform.h>
-#include <platform_def.h>
-
-#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();
-}