diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
commit | 102b0d2daa97dae68d3eed54d8fe37a9cc38a892 (patch) | |
tree | bcf648efac40ca6139842707f0eba5a4496a6dd2 /plat/marvell/armada/common/mss | |
parent | Initial commit. (diff) | |
download | arm-trusted-firmware-102b0d2daa97dae68d3eed54d8fe37a9cc38a892.tar.xz arm-trusted-firmware-102b0d2daa97dae68d3eed54d8fe37a9cc38a892.zip |
Adding upstream version 2.8.0+dfsg.upstream/2.8.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plat/marvell/armada/common/mss')
-rw-r--r-- | plat/marvell/armada/common/mss/mss_common.mk | 20 | ||||
-rw-r--r-- | plat/marvell/armada/common/mss/mss_ipc_drv.c | 113 | ||||
-rw-r--r-- | plat/marvell/armada/common/mss/mss_ipc_drv.h | 120 | ||||
-rw-r--r-- | plat/marvell/armada/common/mss/mss_mem.h | 60 | ||||
-rw-r--r-- | plat/marvell/armada/common/mss/mss_scp_bl2_format.h | 45 | ||||
-rw-r--r-- | plat/marvell/armada/common/mss/mss_scp_bootloader.c | 368 | ||||
-rw-r--r-- | plat/marvell/armada/common/mss/mss_scp_bootloader.h | 20 |
7 files changed, 746 insertions, 0 deletions
diff --git a/plat/marvell/armada/common/mss/mss_common.mk b/plat/marvell/armada/common/mss/mss_common.mk new file mode 100644 index 0000000..4ab4359 --- /dev/null +++ b/plat/marvell/armada/common/mss/mss_common.mk @@ -0,0 +1,20 @@ +# +# Copyright (C) 2018 Marvell International Ltd. +# +# SPDX-License-Identifier: BSD-3-Clause +# https://spdx.org/licenses +# + + +PLAT_MARVELL := plat/marvell/armada +MSS_SOURCE := $(PLAT_MARVELL)/common/mss + +BL2_SOURCES += $(MSS_SOURCE)/mss_scp_bootloader.c \ + $(PLAT_MARVELL)/common/plat_delay_timer.c \ + drivers/delay_timer/delay_timer.c \ + $(MARVELL_DRV) \ + $(BOARD_DIR)/board/marvell_plat_config.c + +BL31_SOURCES += $(MSS_SOURCE)/mss_ipc_drv.c + +PLAT_INCLUDES += -I$(MSS_SOURCE) diff --git a/plat/marvell/armada/common/mss/mss_ipc_drv.c b/plat/marvell/armada/common/mss/mss_ipc_drv.c new file mode 100644 index 0000000..70ccfa5 --- /dev/null +++ b/plat/marvell/armada/common/mss/mss_ipc_drv.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <string.h> + +#include <common/debug.h> +#include <lib/mmio.h> + +#include <plat_marvell.h> +#include <mss_ipc_drv.h> + +#define IPC_MSG_BASE_MASK MVEBU_REGS_BASE_MASK + +#define IPC_CH_NUM_OF_MSG (16) +#define IPC_CH_MSG_IDX (-1) + +unsigned long mv_pm_ipc_msg_base; +unsigned int mv_pm_ipc_queue_size; + +unsigned int msg_sync; +int msg_index = IPC_CH_MSG_IDX; + +/****************************************************************************** + * mss_pm_ipc_init + * + * DESCRIPTION: Initialize PM IPC infrastructure + ****************************************************************************** + */ +int mv_pm_ipc_init(unsigned long ipc_control_addr) +{ + struct mss_pm_ipc_ctrl *ipc_control = + (struct mss_pm_ipc_ctrl *)ipc_control_addr; + + /* Initialize PM IPC control block */ + mv_pm_ipc_msg_base = ipc_control->msg_base_address | + IPC_MSG_BASE_MASK; + mv_pm_ipc_queue_size = ipc_control->queue_size; + + return 0; +} + +/****************************************************************************** + * mv_pm_ipc_queue_addr_get + * + * DESCRIPTION: Returns the IPC queue address + ****************************************************************************** + */ +unsigned int mv_pm_ipc_queue_addr_get(void) +{ + unsigned int addr; + + inv_dcache_range((uint64_t)&msg_index, sizeof(msg_index)); + msg_index = msg_index + 1; + if (msg_index >= IPC_CH_NUM_OF_MSG) + msg_index = 0; + + addr = (unsigned int)(mv_pm_ipc_msg_base + + (msg_index * mv_pm_ipc_queue_size)); + + flush_dcache_range((uint64_t)&msg_index, sizeof(msg_index)); + + return addr; +} + +/****************************************************************************** + * mv_pm_ipc_msg_rx + * + * DESCRIPTION: Retrieve message from IPC channel + ****************************************************************************** + */ +int mv_pm_ipc_msg_rx(unsigned int channel_id, struct mss_pm_ipc_msg *msg) +{ + unsigned int addr = mv_pm_ipc_queue_addr_get(); + + msg->msg_reply = mmio_read_32(addr + IPC_MSG_REPLY_LOC); + + return 0; +} + +/****************************************************************************** + * mv_pm_ipc_msg_tx + * + * DESCRIPTION: Send message via IPC channel + ****************************************************************************** + */ +int mv_pm_ipc_msg_tx(unsigned int channel_id, unsigned int msg_id, + unsigned int cluster_power_state) +{ + unsigned int addr = mv_pm_ipc_queue_addr_get(); + + /* Validate the entry for message placed by the host is free */ + if (mmio_read_32(addr + IPC_MSG_STATE_LOC) == IPC_MSG_FREE) { + inv_dcache_range((uint64_t)&msg_sync, sizeof(msg_sync)); + msg_sync = msg_sync + 1; + flush_dcache_range((uint64_t)&msg_sync, sizeof(msg_sync)); + + mmio_write_32(addr + IPC_MSG_SYNC_ID_LOC, msg_sync); + mmio_write_32(addr + IPC_MSG_ID_LOC, msg_id); + mmio_write_32(addr + IPC_MSG_CPU_ID_LOC, channel_id); + mmio_write_32(addr + IPC_MSG_POWER_STATE_LOC, + cluster_power_state); + mmio_write_32(addr + IPC_MSG_STATE_LOC, IPC_MSG_OCCUPY); + + } else { + ERROR("%s: FAILED\n", __func__); + } + + return 0; +} diff --git a/plat/marvell/armada/common/mss/mss_ipc_drv.h b/plat/marvell/armada/common/mss/mss_ipc_drv.h new file mode 100644 index 0000000..bcb4b2d --- /dev/null +++ b/plat/marvell/armada/common/mss/mss_ipc_drv.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef MSS_IPC_DRV_H +#define MSS_IPC_DRV_H + +#include <lib/psci/psci.h> + +#define MV_PM_FW_IPC_VERSION_MAGIC (0xCA530000) /* Do NOT change */ +/* Increament for each version */ +#define MV_PM_FW_IPC_VERSION_SEQ (0x00000001) +#define MV_PM_FW_IPC_VERSION (MV_PM_FW_IPC_VERSION_MAGIC | \ + MV_PM_FW_IPC_VERSION_SEQ) + +#define IPC_MSG_STATE_LOC (0x0) +#define IPC_MSG_SYNC_ID_LOC (0x4) +#define IPC_MSG_ID_LOC (0x8) +#define IPC_MSG_RET_CH_ID_LOC (0xC) +#define IPC_MSG_CPU_ID_LOC (0x10) +#define IPC_MSG_CLUSTER_ID_LOC (0x14) +#define IPC_MSG_SYSTEM_ID_LOC (0x18) +#define IPC_MSG_POWER_STATE_LOC (0x1C) +#define IPC_MSG_REPLY_LOC (0x20) +#define IPC_MSG_RESERVED_LOC (0x24) + +/* IPC initialization state */ +enum mss_pm_ipc_init_state { + IPC_UN_INITIALIZED = 1, + IPC_INITIALIZED = 2 +}; + +/* IPC queue direction */ +enum mss_pm_ipc_init_msg_dir { + IPC_MSG_TX = 0, + IPC_MSG_RX = 1 +}; + +/* IPC message state */ +enum mss_pm_ipc_msg_state { + IPC_MSG_FREE = 1, + IPC_MSG_OCCUPY = 2 + +}; + +/* IPC control block */ +struct mss_pm_ipc_ctrl { + unsigned int ctrl_base_address; + unsigned int msg_base_address; + unsigned int num_of_channels; + unsigned int channel_size; + unsigned int queue_size; +}; + +/* IPC message types */ +enum mss_pm_msg_id { + PM_IPC_MSG_CPU_SUSPEND = 1, + PM_IPC_MSG_CPU_OFF = 2, + PM_IPC_MSG_CPU_ON = 3, + PM_IPC_MSG_SYSTEM_RESET = 4, + PM_IPC_MSG_SYSTEM_SUSPEND = 5, + PM_IPC_MAX_MSG +}; + +struct mss_pm_ipc_msg { + unsigned int msg_sync_id; /* + * Sync number, validate message + * reply corresponding to message + * received + */ + unsigned int msg_id; /* Message Id */ + unsigned int ret_channel_id; /* IPC channel reply */ + unsigned int cpu_id; /* CPU Id */ + unsigned int cluster_id; /* Cluster Id */ + unsigned int system_id; /* System Id */ + unsigned int power_state; + unsigned int msg_reply; /* Message reply */ +}; + +/* IPC queue */ +struct mss_pm_ipc_queue { + unsigned int state; + struct mss_pm_ipc_msg msg; +}; + +/* IPC channel */ +struct mss_pm_ipc_ch { + struct mss_pm_ipc_queue *tx_queue; + struct mss_pm_ipc_queue *rx_queue; +}; + +/***************************************************************************** + * mv_pm_ipc_init + * + * DESCRIPTION: Initialize PM IPC infrastructure + ***************************************************************************** + */ +int mv_pm_ipc_init(unsigned long ipc_control_addr); + +/***************************************************************************** + * mv_pm_ipc_msg_rx + * + * DESCRIPTION: Retrieve message from IPC channel + ***************************************************************************** + */ +int mv_pm_ipc_msg_rx(unsigned int channel_id, struct mss_pm_ipc_msg *msg); + +/***************************************************************************** + * mv_pm_ipc_msg_tx + * + * DESCRIPTION: Send message via IPC channel + ***************************************************************************** + */ +int mv_pm_ipc_msg_tx(unsigned int channel_id, unsigned int msg_id, + unsigned int cluster_power_state); + +#endif /* MSS_IPC_DRV_H */ diff --git a/plat/marvell/armada/common/mss/mss_mem.h b/plat/marvell/armada/common/mss/mss_mem.h new file mode 100644 index 0000000..5d68ac7 --- /dev/null +++ b/plat/marvell/armada/common/mss/mss_mem.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef MSS_MEM_H +#define MSS_MEM_H + +/* MSS SRAM Memory base */ +#define MSS_SRAM_PM_CONTROL_BASE (MVEBU_REGS_BASE + 0x520000) + +enum mss_pm_ctrl_handshake { + MSS_UN_INITIALIZED = 0, + MSS_COMPATIBILITY_ERROR = 1, + MSS_ACKNOWLEDGMENT = 2, + HOST_ACKNOWLEDGMENT = 3 +}; + +enum mss_pm_ctrl_rtos_env { + MSS_MULTI_PROCESS_ENV = 0, + MSS_SINGLE_PROCESS_ENV = 1, + MSS_MAX_PROCESS_ENV +}; + +struct mss_pm_ctrl_block { + /* This field is used to synchronize the Host + * and MSS initialization sequence + * Valid Values + * 0 - Un-Initialized + * 1 - Compatibility Error + * 2 - MSS Acknowledgment + * 3 - Host Acknowledgment + */ + unsigned int handshake; + + /* + * This field include Host IPC version. Once received by the MSS + * It will be compared to MSS IPC version and set MSS Acknowledge to + * "compatibility error" in case there is no match + */ + unsigned int ipc_version; + unsigned int ipc_base_address; + unsigned int ipc_state; + + /* Following fields defines firmware core architecture */ + unsigned int num_of_cores; + unsigned int num_of_clusters; + unsigned int num_of_cores_per_cluster; + + /* Following fields define pm trace debug base address */ + unsigned int pm_trace_ctrl_base_address; + unsigned int pm_trace_info_base_address; + unsigned int pm_trace_info_core_size; + + unsigned int ctrl_blk_size; +}; + +#endif /* MSS_MEM_H */ diff --git a/plat/marvell/armada/common/mss/mss_scp_bl2_format.h b/plat/marvell/armada/common/mss/mss_scp_bl2_format.h new file mode 100644 index 0000000..90913b0 --- /dev/null +++ b/plat/marvell/armada/common/mss/mss_scp_bl2_format.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef MSS_SCP_BL2_FORMAT_H +#define MSS_SCP_BL2_FORMAT_H + +#define MAX_NR_OF_FILES 8 +#define FILE_MAGIC 0xddd01ff +#define HEADER_VERSION 0x1 + +#define MSS_IDRAM_SIZE 0x10000 /* 64KB */ +#define MSS_SRAM_SIZE 0x8000 /* 32KB */ + +/* Types definitions */ +typedef struct file_header { + /* Magic specific for concatenated file (used for validation) */ + uint32_t magic; + uint32_t nr_of_imgs; /* Number of images concatenated */ +} file_header_t; + +/* Types definitions */ +enum cm3_t { + MSS_AP, + MSS_CP0, + MSS_CP1, + MSS_CP2, + MSS_CP3, + MG_CP0, + MG_CP1, + MG_CP2, +}; + +typedef struct img_header { + uint32_t type; /* CM3 type, can be one of cm3_t */ + uint32_t length; /* Image length */ + uint32_t version; /* For sanity checks and future + * extended functionality + */ +} img_header_t; + +#endif /* MSS_SCP_BL2_FORMAT_H */ diff --git a/plat/marvell/armada/common/mss/mss_scp_bootloader.c b/plat/marvell/armada/common/mss/mss_scp_bootloader.c new file mode 100644 index 0000000..fbede1b --- /dev/null +++ b/plat/marvell/armada/common/mss/mss_scp_bootloader.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#include <assert.h> + +#include <platform_def.h> + +#include <arch_helpers.h> +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <mg_conf_cm3/mg_conf_cm3.h> +#include <lib/mmio.h> + +#include <plat_pm_trace.h> +#include <mss_scp_bootloader.h> +#include <mss_ipc_drv.h> +#include <mss_mem.h> +#include <mss_defs.h> +#include <mss_scp_bl2_format.h> + +#define MSS_DMA_TIMEOUT 1000 +#define MSS_EXTERNAL_SPACE 0x50000000 +#define MSS_EXTERNAL_ADDR_MASK 0xfffffff +#define MSS_INTERNAL_SPACE 0x40000000 +#define MSS_INTERNAL_ADDR_MASK 0x00ffffff + +#define DMA_SIZE 128 + +#define MSS_HANDSHAKE_TIMEOUT 50 + +static int mss_check_image_ready(volatile struct mss_pm_ctrl_block *mss_pm_crtl) +{ + int timeout = MSS_HANDSHAKE_TIMEOUT; + + /* Wait for SCP to signal it's ready */ + while ((mss_pm_crtl->handshake != MSS_ACKNOWLEDGMENT) && + (timeout-- > 0)) + mdelay(1); + + if (mss_pm_crtl->handshake != MSS_ACKNOWLEDGMENT) + return -1; + + mss_pm_crtl->handshake = HOST_ACKNOWLEDGMENT; + + return 0; +} + +static int mss_iram_dma_load(uint32_t src_addr, uint32_t size, + uintptr_t mss_regs) +{ + uint32_t i, loop_num, timeout; + + /* load image to MSS RAM using DMA */ + loop_num = (size / DMA_SIZE) + !!(size % DMA_SIZE); + for (i = 0; i < loop_num; i++) { + /* write source address */ + mmio_write_32(MSS_DMA_SRCBR(mss_regs), + src_addr + (i * DMA_SIZE)); + /* write destination address */ + mmio_write_32(MSS_DMA_DSTBR(mss_regs), (i * DMA_SIZE)); + /* make sure DMA data is ready before triggering it */ + dsb(); + /* set the DMA control register */ + mmio_write_32(MSS_DMA_CTRLR(mss_regs), + ((MSS_DMA_CTRLR_REQ_SET << + MSS_DMA_CTRLR_REQ_OFFSET) | + (DMA_SIZE << MSS_DMA_CTRLR_SIZE_OFFSET))); + /* Poll DMA_ACK at MSS_DMACTLR until it is ready */ + timeout = MSS_DMA_TIMEOUT; + while (timeout > 0U) { + if (((mmio_read_32(MSS_DMA_CTRLR(mss_regs)) >> + MSS_DMA_CTRLR_ACK_OFFSET) & + MSS_DMA_CTRLR_ACK_MASK) + == MSS_DMA_CTRLR_ACK_READY) { + break; + } + udelay(50); + timeout--; + } + if (timeout == 0) { + ERROR("\nMSS DMA failed (timeout)\n"); + return 1; + } + } + return 0; +} + +static int mss_image_load(uint32_t src_addr, uint32_t size, + uintptr_t mss_regs, uintptr_t sram) +{ + uint32_t chunks = 1; /* !sram case */ + uint32_t chunk_num; + int ret; + + /* Check if the img size is not bigger than ID-RAM size of MSS CM3 */ + if (size > MSS_IDRAM_SIZE) { + ERROR("image is too big to fit into MSS CM3 memory\n"); + return 1; + } + + /* The CPx MSS DMA cannot access DRAM directly in secure boot mode + * Copy the MSS FW image to MSS SRAM by the CPU first, then run + * MSS DMA for SRAM to IRAM copy + */ + if (sram != 0) { + chunks = size / MSS_SRAM_SIZE + !!(size % MSS_SRAM_SIZE); + } + + NOTICE("%s Loading MSS FW from addr. 0x%x Size 0x%x to MSS at 0x%lx\n", + sram == 0 ? "" : "SECURELY", src_addr, size, mss_regs); + for (chunk_num = 0; chunk_num < chunks; chunk_num++) { + size_t chunk_size = size; + uint32_t img_src = MSS_EXTERNAL_SPACE | /* no SRAM */ + (src_addr & MSS_EXTERNAL_ADDR_MASK); + + if (sram != 0) { + uintptr_t chunk_source = + src_addr + MSS_SRAM_SIZE * chunk_num; + + if (chunk_num != (size / MSS_SRAM_SIZE)) { + chunk_size = MSS_SRAM_SIZE; + } else { + chunk_size = size % MSS_SRAM_SIZE; + } + + if (chunk_size == 0) { + break; + } + + VERBOSE("Chunk %d -> SRAM 0x%lx from 0x%lx SZ 0x%lx\n", + chunk_num, sram, chunk_source, chunk_size); + memcpy((void *)sram, (void *)chunk_source, chunk_size); + dsb(); + img_src = MSS_INTERNAL_SPACE | + (sram & MSS_INTERNAL_ADDR_MASK); + } + + ret = mss_iram_dma_load(img_src, chunk_size, mss_regs); + if (ret != 0) { + ERROR("MSS FW chunk %d load failed\n", chunk_num); + return ret; + } + } + + bl2_plat_configure_mss_windows(mss_regs); + + if (sram != 0) { + /* Wipe the MSS SRAM after using it as copy buffer */ + memset((void *)sram, 0, MSS_SRAM_SIZE); + NOTICE("CP MSS startup is postponed\n"); + /* FW loaded, but CPU startup postponed until final CP setup */ + mmio_write_32(sram, MSS_FW_READY_MAGIC); + dsb(); + } else { + /* Release M3 from reset */ + mmio_write_32(MSS_M3_RSTCR(mss_regs), + (MSS_M3_RSTCR_RST_OFF << + MSS_M3_RSTCR_RST_OFFSET)); + } + + NOTICE("Done\n"); + + return 0; +} + +/* Load image to MSS AP and do PM related initialization + * Note that this routine is different than other CM3 loading routines, because + * firmware for AP is dedicated for PM and therefore some additional PM + * initialization is required + */ +static int mss_ap_load_image(uintptr_t single_img, + uint32_t image_size, uint32_t ap_idx) +{ + volatile struct mss_pm_ctrl_block *mss_pm_crtl; + int ret; + + /* TODO: add PM Control Info from platform */ + mss_pm_crtl = (struct mss_pm_ctrl_block *)MSS_SRAM_PM_CONTROL_BASE; + mss_pm_crtl->ipc_version = MV_PM_FW_IPC_VERSION; + mss_pm_crtl->num_of_clusters = PLAT_MARVELL_CLUSTER_COUNT; + mss_pm_crtl->num_of_cores_per_cluster = + PLAT_MARVELL_CLUSTER_CORE_COUNT; + mss_pm_crtl->num_of_cores = PLAT_MARVELL_CLUSTER_COUNT * + PLAT_MARVELL_CLUSTER_CORE_COUNT; + mss_pm_crtl->pm_trace_ctrl_base_address = AP_MSS_ATF_CORE_CTRL_BASE; + mss_pm_crtl->pm_trace_info_base_address = AP_MSS_ATF_CORE_INFO_BASE; + mss_pm_crtl->pm_trace_info_core_size = AP_MSS_ATF_CORE_INFO_SIZE; + VERBOSE("MSS Control Block = 0x%x\n", MSS_SRAM_PM_CONTROL_BASE); + VERBOSE("mss_pm_crtl->ipc_version = 0x%x\n", + mss_pm_crtl->ipc_version); + VERBOSE("mss_pm_crtl->num_of_cores = 0x%x\n", + mss_pm_crtl->num_of_cores); + VERBOSE("mss_pm_crtl->num_of_clusters = 0x%x\n", + mss_pm_crtl->num_of_clusters); + VERBOSE("mss_pm_crtl->num_of_cores_per_cluster = 0x%x\n", + mss_pm_crtl->num_of_cores_per_cluster); + VERBOSE("mss_pm_crtl->pm_trace_ctrl_base_address = 0x%x\n", + mss_pm_crtl->pm_trace_ctrl_base_address); + VERBOSE("mss_pm_crtl->pm_trace_info_base_address = 0x%x\n", + mss_pm_crtl->pm_trace_info_base_address); + VERBOSE("mss_pm_crtl->pm_trace_info_core_size = 0x%x\n", + mss_pm_crtl->pm_trace_info_core_size); + + /* TODO: add checksum to image */ + VERBOSE("Send info about the SCP_BL2 image to be transferred to SCP\n"); + + ret = mss_image_load(single_img, image_size, + bl2_plat_get_ap_mss_regs(ap_idx), 0); + if (ret != 0) { + ERROR("SCP Image load failed\n"); + return -1; + } + + /* check that the image was loaded successfully */ + ret = mss_check_image_ready(mss_pm_crtl); + if (ret != 0) + NOTICE("SCP Image doesn't contain PM firmware\n"); + + return 0; +} + +/* Load CM3 image (single_img) to CM3 pointed by cm3_type */ +static int load_img_to_cm3(enum cm3_t cm3_type, + uintptr_t single_img, uint32_t image_size) +{ + int ret, ap_idx, cp_index; + uint32_t ap_count = bl2_plat_get_ap_count(); + + switch (cm3_type) { + case MSS_AP: + for (ap_idx = 0; ap_idx < ap_count; ap_idx++) { + NOTICE("Load image to AP%d MSS\n", ap_idx); + ret = mss_ap_load_image(single_img, image_size, ap_idx); + if (ret != 0) + return ret; + } + break; + case MSS_CP0: + case MSS_CP1: + case MSS_CP2: + case MSS_CP3: + /* MSS_AP = 0 + * MSS_CP1 = 1 + * . + * . + * MSS_CP3 = 4 + * Actual CP index is MSS_CPX - 1 + */ + cp_index = cm3_type - 1; + for (ap_idx = 0; ap_idx < ap_count; ap_idx++) { + /* Check if we should load this image + * according to number of CPs + */ + if (bl2_plat_get_cp_count(ap_idx) <= cp_index) { + NOTICE("Skipping MSS CP%d related image\n", + cp_index); + break; + } + + NOTICE("Load image to CP%d MSS AP%d\n", + cp_index, ap_idx); + ret = mss_image_load(single_img, image_size, + bl2_plat_get_cp_mss_regs( + ap_idx, cp_index), + bl2_plat_get_cp_mss_sram( + ap_idx, cp_index)); + if (ret != 0) { + ERROR("SCP Image load failed\n"); + return -1; + } + } + break; + case MG_CP0: + case MG_CP1: + case MG_CP2: + cp_index = cm3_type - MG_CP0; + if (bl2_plat_get_cp_count(0) <= cp_index) { + NOTICE("Skipping MG CP%d related image\n", + cp_index); + break; + } + NOTICE("Load image to CP%d MG\n", cp_index); + ret = mg_image_load(single_img, image_size, cp_index); + if (ret != 0) { + ERROR("SCP Image load failed\n"); + return -1; + } + break; + default: + ERROR("SCP_BL2 wrong img format (cm3_type=%d)\n", cm3_type); + break; + } + + return 0; +} + +/* The Armada 8K has 5 service CPUs and Armada 7K has 3. Therefore it was + * required to provide a method for loading firmware to all of the service CPUs. + * To achieve that, the scp_bl2 image in fact is file containing up to 5 + * concatenated firmwares and this routine splits concatenated image into single + * images dedicated for appropriate service CPU and then load them. + */ +static int split_and_load_bl2_image(void *image) +{ + file_header_t *file_hdr; + img_header_t *img_hdr; + uintptr_t single_img; + int i; + + file_hdr = (file_header_t *)image; + + if (file_hdr->magic != FILE_MAGIC) { + ERROR("SCP_BL2 wrong img format\n"); + return -1; + } + + if (file_hdr->nr_of_imgs > MAX_NR_OF_FILES) { + ERROR("SCP_BL2 concatenated image contains too many images\n"); + return -1; + } + + img_hdr = (img_header_t *)((uintptr_t)image + sizeof(file_header_t)); + single_img = (uintptr_t)image + sizeof(file_header_t) + + sizeof(img_header_t) * file_hdr->nr_of_imgs; + + NOTICE("SCP_BL2 contains %d concatenated images\n", + file_hdr->nr_of_imgs); + for (i = 0; i < file_hdr->nr_of_imgs; i++) { + + /* Before loading make sanity check on header */ + if (img_hdr->version != HEADER_VERSION) { + ERROR("Wrong header, img corrupted exiting\n"); + return -1; + } + + load_img_to_cm3(img_hdr->type, single_img, img_hdr->length); + + /* Prepare offsets for next run */ + single_img += img_hdr->length; + img_hdr++; + } + + return 0; +} + +int scp_bootloader_transfer(void *image, unsigned int image_size) +{ +#ifdef SCP_BL2_BASE + assert((uintptr_t) image == SCP_BL2_BASE); +#endif + + VERBOSE("Concatenated img size %d\n", image_size); + + if (image_size == 0) { + ERROR("SCP_BL2 image size can't be 0 (current size = 0x%x)\n", + image_size); + return -1; + } + + if (split_and_load_bl2_image(image)) + return -1; + + return 0; +} diff --git a/plat/marvell/armada/common/mss/mss_scp_bootloader.h b/plat/marvell/armada/common/mss/mss_scp_bootloader.h new file mode 100644 index 0000000..d65354a --- /dev/null +++ b/plat/marvell/armada/common/mss/mss_scp_bootloader.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +#ifndef MSS_SCP_BOOTLOADER_H +#define MSS_SCP_BOOTLOADER_H + +int scp_bootloader_transfer(void *image, unsigned int image_size); +uintptr_t bl2_plat_get_cp_mss_regs(int ap_idx, int cp_idx); +uintptr_t bl2_plat_get_cp_mss_sram(int ap_idx, int cp_idx); +uintptr_t bl2_plat_get_ap_mss_regs(int ap_idx); +uint32_t bl2_plat_get_cp_count(int ap_idx); +uint32_t bl2_plat_get_ap_count(void); +void bl2_plat_configure_mss_windows(uintptr_t mss_regs); +int bl2_plat_mss_check_image_ready(void); + +#endif /* MSS_SCP_BOOTLOADER_H */ |