GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
+20
@@ -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)
|
||||
+113
@@ -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;
|
||||
}
|
||||
+120
@@ -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 */
|
||||
+60
@@ -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 */
|
||||
+45
@@ -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 */
|
||||
+368
@@ -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;
|
||||
}
|
||||
+20
@@ -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 */
|
||||
Reference in New Issue
Block a user