plat: marvell: Add common ARMADA platform components

Add common Marvell ARMADA platform components.
This patch also includes common components for Marvell
ARMADA 8K platforms.

Change-Id: I42192fdc6525a42e46b3ac2ad63c83db9bcbfeaf
Signed-off-by: Hanna Hawa <hannah@marvell.com>
Signed-off-by: Konstantin Porotchkin <kostap@marvell.com>
diff --git a/plat/marvell/common/mss/mss_common.mk b/plat/marvell/common/mss/mss_common.mk
new file mode 100644
index 0000000..898b6dc
--- /dev/null
+++ b/plat/marvell/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
+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)					\
+				$(PLAT_FAMILY_BASE)/$(PLAT)/board/marvell_plat_config.c
+
+BL31_SOURCES		+=	$(MSS_SOURCE)/mss_ipc_drv.c
+
+PLAT_INCLUDES           +=      -I$(MSS_SOURCE)
diff --git a/plat/marvell/common/mss/mss_ipc_drv.c b/plat/marvell/common/mss/mss_ipc_drv.c
new file mode 100644
index 0000000..731c315
--- /dev/null
+++ b/plat/marvell/common/mss/mss_ipc_drv.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ * https://spdx.org/licenses
+ */
+
+#include <plat_marvell.h>
+#include <debug.h>
+#include <string.h>
+#include <mss_ipc_drv.h>
+#include <mmio.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/common/mss/mss_ipc_drv.h b/plat/marvell/common/mss/mss_ipc_drv.h
new file mode 100644
index 0000000..28eb907
--- /dev/null
+++ b/plat/marvell/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 __PM_IPC_DRV_H
+#define __PM_IPC_DRV_H
+
+#include <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 /* __PM_IPC_DRV_H */
diff --git a/plat/marvell/common/mss/mss_mem.h b/plat/marvell/common/mss/mss_mem.h
new file mode 100644
index 0000000..efff59e
--- /dev/null
+++ b/plat/marvell/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_PM_MEM_H
+#define __MSS_PM_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_PM_MEM_H */
diff --git a/plat/marvell/common/mss/mss_scp_bl2_format.h b/plat/marvell/common/mss/mss_scp_bl2_format.h
new file mode 100644
index 0000000..c04df72
--- /dev/null
+++ b/plat/marvell/common/mss/mss_scp_bl2_format.h
@@ -0,0 +1,44 @@
+/*
+ * 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	5
+#define FILE_MAGIC	0xddd01ff
+#define HEADER_VERSION	0x1
+
+#define MSS_IDRAM_SIZE	0x10000 /* 64KB */
+#define MG_SRAM_SIZE	0x20000 /* 128KB */
+
+/* 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,
+};
+
+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/common/mss/mss_scp_bootloader.c b/plat/marvell/common/mss/mss_scp_bootloader.c
new file mode 100644
index 0000000..334fcfc
--- /dev/null
+++ b/plat/marvell/common/mss/mss_scp_bootloader.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ * https://spdx.org/licenses
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <mmio.h>
+#include <arch_helpers.h> /* for cache maintanance operations */
+#include <platform_def.h>
+#include <delay_timer.h>
+
+#include <plat_pm_trace.h>
+#include <mss_scp_bootloader.h>
+#include <mss_ipc_drv.h>
+#include <mss_mem.h>
+#include <mss_scp_bl2_format.h>
+
+#define MSS_DMA_SRCBR(base)		(base + 0xC0)
+#define MSS_DMA_DSTBR(base)		(base + 0xC4)
+#define MSS_DMA_CTRLR(base)		(base + 0xC8)
+#define MSS_M3_RSTCR(base)		(base + 0xFC)
+
+#define MSS_DMA_CTRLR_SIZE_OFFSET	(0)
+#define MSS_DMA_CTRLR_REQ_OFFSET	(15)
+#define MSS_DMA_CTRLR_REQ_SET		(1)
+#define MSS_DMA_CTRLR_ACK_OFFSET	(12)
+#define MSS_DMA_CTRLR_ACK_MASK		(0x1)
+#define MSS_DMA_CTRLR_ACK_READY		(1)
+#define MSS_M3_RSTCR_RST_OFFSET		(0)
+#define MSS_M3_RSTCR_RST_OFF		(1)
+
+#define MSS_DMA_TIMEOUT			1000
+#define MSS_EXTERNAL_SPACE		0x50000000
+#define MSS_EXTERNAL_ADDR_MASK		0xfffffff
+
+#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_image_load(uint32_t src_addr, uint32_t size, uintptr_t mss_regs)
+{
+	uint32_t i, loop_num, timeout;
+
+	/* 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;
+	}
+
+	NOTICE("Loading MSS image from addr. 0x%x Size 0x%x to MSS at 0x%lx\n",
+	       src_addr, size, mss_regs);
+	/* load image to MSS RAM using DMA */
+	loop_num = (size / DMA_SIZE) + (((size & (DMA_SIZE - 1)) == 0) ? 0 : 1);
+
+	for (i = 0; i < loop_num; i++) {
+		/* write destination and source addresses */
+		mmio_write_32(MSS_DMA_SRCBR(mss_regs),
+			      MSS_EXTERNAL_SPACE |
+			      ((src_addr & MSS_EXTERNAL_ADDR_MASK) +
+			      (i * DMA_SIZE)));
+		mmio_write_32(MSS_DMA_DSTBR(mss_regs), (i * DMA_SIZE));
+
+		dsb(); /* make sure DMA data is ready before triggering it */
+
+		/* 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) {
+			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("\nDMA failed to load MSS image\n");
+			return 1;
+		}
+	}
+
+	bl2_plat_configure_mss_windows(mss_regs);
+
+	/* 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));
+	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));
+			if (ret != 0) {
+				ERROR("SCP Image load failed\n");
+				return -1;
+			}
+		}
+		break;
+	case MG_CP0:
+		/* TODO: */
+		NOTICE("Load image to CP0 MG not supported\n");
+		break;
+	case MG_CP1:
+		/* TODO: */
+		NOTICE("Load image to CP1 MG not supported\n");
+		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 to 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/common/mss/mss_scp_bootloader.h b/plat/marvell/common/mss/mss_scp_bootloader.h
new file mode 100644
index 0000000..67c387a
--- /dev/null
+++ b/plat/marvell/common/mss/mss_scp_bootloader.h
@@ -0,0 +1,19 @@
+/*
+ * 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_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__ */