xref: /rk3399_ARM-atf/plat/marvell/armada/common/mss/mss_scp_bootloader.c (revision 2939f68add3269bf0e67f7a25c4a1b404a25c616)
1a2847172SGrzegorz Jaszczyk /*
2a2847172SGrzegorz Jaszczyk  * Copyright (C) 2018 Marvell International Ltd.
3a2847172SGrzegorz Jaszczyk  *
4a2847172SGrzegorz Jaszczyk  * SPDX-License-Identifier:     BSD-3-Clause
5a2847172SGrzegorz Jaszczyk  * https://spdx.org/licenses
6a2847172SGrzegorz Jaszczyk  */
7a2847172SGrzegorz Jaszczyk 
8a2847172SGrzegorz Jaszczyk #include <assert.h>
9a2847172SGrzegorz Jaszczyk 
10a2847172SGrzegorz Jaszczyk #include <platform_def.h>
11a2847172SGrzegorz Jaszczyk 
12a2847172SGrzegorz Jaszczyk #include <arch_helpers.h>
13a2847172SGrzegorz Jaszczyk #include <common/debug.h>
14a2847172SGrzegorz Jaszczyk #include <drivers/delay_timer.h>
150081cdd1SGrzegorz Jaszczyk #include <mg_conf_cm3/mg_conf_cm3.h>
16a2847172SGrzegorz Jaszczyk #include <lib/mmio.h>
17a2847172SGrzegorz Jaszczyk 
18a2847172SGrzegorz Jaszczyk #include <plat_pm_trace.h>
19a2847172SGrzegorz Jaszczyk #include <mss_scp_bootloader.h>
20a2847172SGrzegorz Jaszczyk #include <mss_ipc_drv.h>
21a2847172SGrzegorz Jaszczyk #include <mss_mem.h>
22*b5a06637SKonstantin Porotchkin #include <mss_defs.h>
23a2847172SGrzegorz Jaszczyk #include <mss_scp_bl2_format.h>
24a2847172SGrzegorz Jaszczyk 
25a2847172SGrzegorz Jaszczyk #define MSS_DMA_TIMEOUT			1000
26a2847172SGrzegorz Jaszczyk #define MSS_EXTERNAL_SPACE		0x50000000
27a2847172SGrzegorz Jaszczyk #define MSS_EXTERNAL_ADDR_MASK		0xfffffff
2857870747SKonstantin Porotchkin #define MSS_INTERNAL_SPACE		0x40000000
2957870747SKonstantin Porotchkin #define MSS_INTERNAL_ADDR_MASK		0x00ffffff
30a2847172SGrzegorz Jaszczyk 
31a2847172SGrzegorz Jaszczyk #define DMA_SIZE			128
32a2847172SGrzegorz Jaszczyk 
33a2847172SGrzegorz Jaszczyk #define MSS_HANDSHAKE_TIMEOUT		50
34a2847172SGrzegorz Jaszczyk 
mss_check_image_ready(volatile struct mss_pm_ctrl_block * mss_pm_crtl)35a2847172SGrzegorz Jaszczyk static int mss_check_image_ready(volatile struct mss_pm_ctrl_block *mss_pm_crtl)
36a2847172SGrzegorz Jaszczyk {
37a2847172SGrzegorz Jaszczyk 	int timeout = MSS_HANDSHAKE_TIMEOUT;
38a2847172SGrzegorz Jaszczyk 
39a2847172SGrzegorz Jaszczyk 	/* Wait for SCP to signal it's ready */
40a2847172SGrzegorz Jaszczyk 	while ((mss_pm_crtl->handshake != MSS_ACKNOWLEDGMENT) &&
41a2847172SGrzegorz Jaszczyk 						(timeout-- > 0))
42a2847172SGrzegorz Jaszczyk 		mdelay(1);
43a2847172SGrzegorz Jaszczyk 
44a2847172SGrzegorz Jaszczyk 	if (mss_pm_crtl->handshake != MSS_ACKNOWLEDGMENT)
45a2847172SGrzegorz Jaszczyk 		return -1;
46a2847172SGrzegorz Jaszczyk 
47a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->handshake = HOST_ACKNOWLEDGMENT;
48a2847172SGrzegorz Jaszczyk 
49a2847172SGrzegorz Jaszczyk 	return 0;
50a2847172SGrzegorz Jaszczyk }
51a2847172SGrzegorz Jaszczyk 
mss_iram_dma_load(uint32_t src_addr,uint32_t size,uintptr_t mss_regs)5257870747SKonstantin Porotchkin static int mss_iram_dma_load(uint32_t src_addr, uint32_t size,
5357870747SKonstantin Porotchkin 			     uintptr_t mss_regs)
54a2847172SGrzegorz Jaszczyk {
55a2847172SGrzegorz Jaszczyk 	uint32_t i, loop_num, timeout;
56a2847172SGrzegorz Jaszczyk 
5757870747SKonstantin Porotchkin 	/* load image to MSS RAM using DMA */
5857870747SKonstantin Porotchkin 	loop_num = (size / DMA_SIZE) + !!(size % DMA_SIZE);
5957870747SKonstantin Porotchkin 	for (i = 0; i < loop_num; i++) {
6057870747SKonstantin Porotchkin 		/* write source address */
6157870747SKonstantin Porotchkin 		mmio_write_32(MSS_DMA_SRCBR(mss_regs),
6257870747SKonstantin Porotchkin 			      src_addr + (i * DMA_SIZE));
6357870747SKonstantin Porotchkin 		/* write destination address */
6457870747SKonstantin Porotchkin 		mmio_write_32(MSS_DMA_DSTBR(mss_regs), (i * DMA_SIZE));
6557870747SKonstantin Porotchkin 		/* make sure DMA data is ready before triggering it */
6657870747SKonstantin Porotchkin 		dsb();
6757870747SKonstantin Porotchkin 		/* set the DMA control register */
6857870747SKonstantin Porotchkin 		mmio_write_32(MSS_DMA_CTRLR(mss_regs),
6957870747SKonstantin Porotchkin 			      ((MSS_DMA_CTRLR_REQ_SET <<
7057870747SKonstantin Porotchkin 				MSS_DMA_CTRLR_REQ_OFFSET) |
7157870747SKonstantin Porotchkin 			      (DMA_SIZE << MSS_DMA_CTRLR_SIZE_OFFSET)));
7257870747SKonstantin Porotchkin 		/* Poll DMA_ACK at MSS_DMACTLR until it is ready */
7357870747SKonstantin Porotchkin 		timeout = MSS_DMA_TIMEOUT;
7457870747SKonstantin Porotchkin 		while (timeout > 0U) {
75dceac436SKonstantin Porotchkin 			if (((mmio_read_32(MSS_DMA_CTRLR(mss_regs)) >>
76dceac436SKonstantin Porotchkin 					  MSS_DMA_CTRLR_ACK_OFFSET) &
77dceac436SKonstantin Porotchkin 					  MSS_DMA_CTRLR_ACK_MASK)
7857870747SKonstantin Porotchkin 					  == MSS_DMA_CTRLR_ACK_READY) {
7957870747SKonstantin Porotchkin 				break;
8057870747SKonstantin Porotchkin 			}
8157870747SKonstantin Porotchkin 			udelay(50);
8257870747SKonstantin Porotchkin 			timeout--;
8357870747SKonstantin Porotchkin 		}
8457870747SKonstantin Porotchkin 		if (timeout == 0) {
8557870747SKonstantin Porotchkin 			ERROR("\nMSS DMA failed (timeout)\n");
8657870747SKonstantin Porotchkin 			return 1;
8757870747SKonstantin Porotchkin 		}
8857870747SKonstantin Porotchkin 	}
8957870747SKonstantin Porotchkin 	return 0;
9057870747SKonstantin Porotchkin }
9157870747SKonstantin Porotchkin 
mss_image_load(uint32_t src_addr,uint32_t size,uintptr_t mss_regs,uintptr_t sram)9257870747SKonstantin Porotchkin static int mss_image_load(uint32_t src_addr, uint32_t size,
9357870747SKonstantin Porotchkin 			  uintptr_t mss_regs, uintptr_t sram)
9457870747SKonstantin Porotchkin {
9557870747SKonstantin Porotchkin 	uint32_t chunks = 1; /* !sram case */
9657870747SKonstantin Porotchkin 	uint32_t chunk_num;
9757870747SKonstantin Porotchkin 	int ret;
9857870747SKonstantin Porotchkin 
99a2847172SGrzegorz Jaszczyk 	/* Check if the img size is not bigger than ID-RAM size of MSS CM3 */
100a2847172SGrzegorz Jaszczyk 	if (size > MSS_IDRAM_SIZE) {
101a2847172SGrzegorz Jaszczyk 		ERROR("image is too big to fit into MSS CM3 memory\n");
102a2847172SGrzegorz Jaszczyk 		return 1;
103a2847172SGrzegorz Jaszczyk 	}
104a2847172SGrzegorz Jaszczyk 
10557870747SKonstantin Porotchkin 	/* The CPx MSS DMA cannot access DRAM directly in secure boot mode
10657870747SKonstantin Porotchkin 	 * Copy the MSS FW image to MSS SRAM by the CPU first, then run
10757870747SKonstantin Porotchkin 	 * MSS DMA for SRAM to IRAM copy
10857870747SKonstantin Porotchkin 	 */
10957870747SKonstantin Porotchkin 	if (sram != 0) {
11057870747SKonstantin Porotchkin 		chunks = size / MSS_SRAM_SIZE + !!(size % MSS_SRAM_SIZE);
11157870747SKonstantin Porotchkin 	}
112a2847172SGrzegorz Jaszczyk 
11357870747SKonstantin Porotchkin 	NOTICE("%s Loading MSS FW from addr. 0x%x Size 0x%x to MSS at 0x%lx\n",
11457870747SKonstantin Porotchkin 	       sram == 0 ? "" : "SECURELY", src_addr, size, mss_regs);
11557870747SKonstantin Porotchkin 	for (chunk_num = 0; chunk_num < chunks; chunk_num++) {
11657870747SKonstantin Porotchkin 		size_t chunk_size = size;
11757870747SKonstantin Porotchkin 		uint32_t img_src = MSS_EXTERNAL_SPACE | /* no SRAM */
11857870747SKonstantin Porotchkin 				   (src_addr & MSS_EXTERNAL_ADDR_MASK);
119a2847172SGrzegorz Jaszczyk 
12057870747SKonstantin Porotchkin 		if (sram != 0) {
12157870747SKonstantin Porotchkin 			uintptr_t chunk_source =
12257870747SKonstantin Porotchkin 				  src_addr + MSS_SRAM_SIZE * chunk_num;
123a2847172SGrzegorz Jaszczyk 
12457870747SKonstantin Porotchkin 			if (chunk_num != (size / MSS_SRAM_SIZE)) {
12557870747SKonstantin Porotchkin 				chunk_size = MSS_SRAM_SIZE;
12657870747SKonstantin Porotchkin 			} else {
12757870747SKonstantin Porotchkin 				chunk_size =  size % MSS_SRAM_SIZE;
12857870747SKonstantin Porotchkin 			}
129a2847172SGrzegorz Jaszczyk 
13057870747SKonstantin Porotchkin 			if (chunk_size == 0) {
131a2847172SGrzegorz Jaszczyk 				break;
132a2847172SGrzegorz Jaszczyk 			}
133a2847172SGrzegorz Jaszczyk 
13457870747SKonstantin Porotchkin 			VERBOSE("Chunk %d -> SRAM 0x%lx from 0x%lx SZ 0x%lx\n",
13557870747SKonstantin Porotchkin 				chunk_num, sram, chunk_source, chunk_size);
13657870747SKonstantin Porotchkin 			memcpy((void *)sram, (void *)chunk_source, chunk_size);
13757870747SKonstantin Porotchkin 			dsb();
13857870747SKonstantin Porotchkin 			img_src = MSS_INTERNAL_SPACE |
13957870747SKonstantin Porotchkin 				  (sram & MSS_INTERNAL_ADDR_MASK);
140a2847172SGrzegorz Jaszczyk 		}
141a2847172SGrzegorz Jaszczyk 
14257870747SKonstantin Porotchkin 		ret = mss_iram_dma_load(img_src, chunk_size, mss_regs);
14357870747SKonstantin Porotchkin 		if (ret != 0) {
14457870747SKonstantin Porotchkin 			ERROR("MSS FW chunk %d load failed\n", chunk_num);
14557870747SKonstantin Porotchkin 			return ret;
146a2847172SGrzegorz Jaszczyk 		}
147a2847172SGrzegorz Jaszczyk 	}
148a2847172SGrzegorz Jaszczyk 
149a2847172SGrzegorz Jaszczyk 	bl2_plat_configure_mss_windows(mss_regs);
150a2847172SGrzegorz Jaszczyk 
151*b5a06637SKonstantin Porotchkin 	if (sram != 0) {
1525a9f5890SKonstantin Porotchkin 		/* Wipe the MSS SRAM after using it as copy buffer */
1535a9f5890SKonstantin Porotchkin 		memset((void *)sram, 0, MSS_SRAM_SIZE);
154*b5a06637SKonstantin Porotchkin 		NOTICE("CP MSS startup is postponed\n");
155*b5a06637SKonstantin Porotchkin 		/* FW loaded, but CPU startup postponed until final CP setup */
156*b5a06637SKonstantin Porotchkin 		mmio_write_32(sram, MSS_FW_READY_MAGIC);
157*b5a06637SKonstantin Porotchkin 		dsb();
158*b5a06637SKonstantin Porotchkin 	} else {
159a2847172SGrzegorz Jaszczyk 		/* Release M3 from reset */
16057870747SKonstantin Porotchkin 		mmio_write_32(MSS_M3_RSTCR(mss_regs),
161*b5a06637SKonstantin Porotchkin 			      (MSS_M3_RSTCR_RST_OFF <<
162*b5a06637SKonstantin Porotchkin 			       MSS_M3_RSTCR_RST_OFFSET));
163*b5a06637SKonstantin Porotchkin 	}
164a2847172SGrzegorz Jaszczyk 
165a2847172SGrzegorz Jaszczyk 	NOTICE("Done\n");
166a2847172SGrzegorz Jaszczyk 
167a2847172SGrzegorz Jaszczyk 	return 0;
168a2847172SGrzegorz Jaszczyk }
169a2847172SGrzegorz Jaszczyk 
170a2847172SGrzegorz Jaszczyk /* Load image to MSS AP and do PM related initialization
171a2847172SGrzegorz Jaszczyk  * Note that this routine is different than other CM3 loading routines, because
172a2847172SGrzegorz Jaszczyk  * firmware for AP is dedicated for PM and therefore some additional PM
173a2847172SGrzegorz Jaszczyk  * initialization is required
174a2847172SGrzegorz Jaszczyk  */
mss_ap_load_image(uintptr_t single_img,uint32_t image_size,uint32_t ap_idx)175a2847172SGrzegorz Jaszczyk static int mss_ap_load_image(uintptr_t single_img,
176a2847172SGrzegorz Jaszczyk 			     uint32_t image_size, uint32_t ap_idx)
177a2847172SGrzegorz Jaszczyk {
178a2847172SGrzegorz Jaszczyk 	volatile struct mss_pm_ctrl_block *mss_pm_crtl;
179a2847172SGrzegorz Jaszczyk 	int ret;
180a2847172SGrzegorz Jaszczyk 
181a2847172SGrzegorz Jaszczyk 	/* TODO: add PM Control Info from platform */
182a2847172SGrzegorz Jaszczyk 	mss_pm_crtl = (struct mss_pm_ctrl_block *)MSS_SRAM_PM_CONTROL_BASE;
183a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->ipc_version                = MV_PM_FW_IPC_VERSION;
184a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->num_of_clusters            = PLAT_MARVELL_CLUSTER_COUNT;
185a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->num_of_cores_per_cluster   =
186a2847172SGrzegorz Jaszczyk 						PLAT_MARVELL_CLUSTER_CORE_COUNT;
187a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->num_of_cores               = PLAT_MARVELL_CLUSTER_COUNT *
188a2847172SGrzegorz Jaszczyk 						PLAT_MARVELL_CLUSTER_CORE_COUNT;
189a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->pm_trace_ctrl_base_address = AP_MSS_ATF_CORE_CTRL_BASE;
190a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->pm_trace_info_base_address = AP_MSS_ATF_CORE_INFO_BASE;
191a2847172SGrzegorz Jaszczyk 	mss_pm_crtl->pm_trace_info_core_size    = AP_MSS_ATF_CORE_INFO_SIZE;
192a2847172SGrzegorz Jaszczyk 	VERBOSE("MSS Control Block = 0x%x\n", MSS_SRAM_PM_CONTROL_BASE);
193a2847172SGrzegorz Jaszczyk 	VERBOSE("mss_pm_crtl->ipc_version                = 0x%x\n",
194a2847172SGrzegorz Jaszczyk 		mss_pm_crtl->ipc_version);
195a2847172SGrzegorz Jaszczyk 	VERBOSE("mss_pm_crtl->num_of_cores               = 0x%x\n",
196a2847172SGrzegorz Jaszczyk 		mss_pm_crtl->num_of_cores);
197a2847172SGrzegorz Jaszczyk 	VERBOSE("mss_pm_crtl->num_of_clusters            = 0x%x\n",
198a2847172SGrzegorz Jaszczyk 		mss_pm_crtl->num_of_clusters);
199a2847172SGrzegorz Jaszczyk 	VERBOSE("mss_pm_crtl->num_of_cores_per_cluster   = 0x%x\n",
200a2847172SGrzegorz Jaszczyk 		mss_pm_crtl->num_of_cores_per_cluster);
201a2847172SGrzegorz Jaszczyk 	VERBOSE("mss_pm_crtl->pm_trace_ctrl_base_address = 0x%x\n",
202a2847172SGrzegorz Jaszczyk 		mss_pm_crtl->pm_trace_ctrl_base_address);
203a2847172SGrzegorz Jaszczyk 	VERBOSE("mss_pm_crtl->pm_trace_info_base_address = 0x%x\n",
204a2847172SGrzegorz Jaszczyk 		mss_pm_crtl->pm_trace_info_base_address);
205a2847172SGrzegorz Jaszczyk 	VERBOSE("mss_pm_crtl->pm_trace_info_core_size    = 0x%x\n",
206a2847172SGrzegorz Jaszczyk 		mss_pm_crtl->pm_trace_info_core_size);
207a2847172SGrzegorz Jaszczyk 
208a2847172SGrzegorz Jaszczyk 	/* TODO: add checksum to image */
209a2847172SGrzegorz Jaszczyk 	VERBOSE("Send info about the SCP_BL2 image to be transferred to SCP\n");
210a2847172SGrzegorz Jaszczyk 
211a2847172SGrzegorz Jaszczyk 	ret = mss_image_load(single_img, image_size,
21257870747SKonstantin Porotchkin 			     bl2_plat_get_ap_mss_regs(ap_idx), 0);
213a2847172SGrzegorz Jaszczyk 	if (ret != 0) {
214a2847172SGrzegorz Jaszczyk 		ERROR("SCP Image load failed\n");
215a2847172SGrzegorz Jaszczyk 		return -1;
216a2847172SGrzegorz Jaszczyk 	}
217a2847172SGrzegorz Jaszczyk 
218a2847172SGrzegorz Jaszczyk 	/* check that the image was loaded successfully */
219a2847172SGrzegorz Jaszczyk 	ret = mss_check_image_ready(mss_pm_crtl);
220a2847172SGrzegorz Jaszczyk 	if (ret != 0)
221a2847172SGrzegorz Jaszczyk 		NOTICE("SCP Image doesn't contain PM firmware\n");
222a2847172SGrzegorz Jaszczyk 
223a2847172SGrzegorz Jaszczyk 	return 0;
224a2847172SGrzegorz Jaszczyk }
225a2847172SGrzegorz Jaszczyk 
226a2847172SGrzegorz Jaszczyk /* Load CM3 image (single_img) to CM3 pointed by cm3_type */
load_img_to_cm3(enum cm3_t cm3_type,uintptr_t single_img,uint32_t image_size)227a2847172SGrzegorz Jaszczyk static int load_img_to_cm3(enum cm3_t cm3_type,
228a2847172SGrzegorz Jaszczyk 			   uintptr_t single_img, uint32_t image_size)
229a2847172SGrzegorz Jaszczyk {
230a2847172SGrzegorz Jaszczyk 	int ret, ap_idx, cp_index;
231a2847172SGrzegorz Jaszczyk 	uint32_t ap_count = bl2_plat_get_ap_count();
232a2847172SGrzegorz Jaszczyk 
233a2847172SGrzegorz Jaszczyk 	switch (cm3_type) {
234a2847172SGrzegorz Jaszczyk 	case MSS_AP:
235a2847172SGrzegorz Jaszczyk 		for (ap_idx = 0; ap_idx < ap_count; ap_idx++) {
236a2847172SGrzegorz Jaszczyk 			NOTICE("Load image to AP%d MSS\n", ap_idx);
237a2847172SGrzegorz Jaszczyk 			ret = mss_ap_load_image(single_img, image_size, ap_idx);
238a2847172SGrzegorz Jaszczyk 			if (ret != 0)
239a2847172SGrzegorz Jaszczyk 				return ret;
240a2847172SGrzegorz Jaszczyk 		}
241a2847172SGrzegorz Jaszczyk 		break;
242a2847172SGrzegorz Jaszczyk 	case MSS_CP0:
243a2847172SGrzegorz Jaszczyk 	case MSS_CP1:
244a2847172SGrzegorz Jaszczyk 	case MSS_CP2:
245a2847172SGrzegorz Jaszczyk 	case MSS_CP3:
246a2847172SGrzegorz Jaszczyk 		/* MSS_AP = 0
247a2847172SGrzegorz Jaszczyk 		 * MSS_CP1 = 1
248a2847172SGrzegorz Jaszczyk 		 * .
249a2847172SGrzegorz Jaszczyk 		 * .
250a2847172SGrzegorz Jaszczyk 		 * MSS_CP3 = 4
251a2847172SGrzegorz Jaszczyk 		 * Actual CP index is MSS_CPX - 1
252a2847172SGrzegorz Jaszczyk 		 */
253a2847172SGrzegorz Jaszczyk 		cp_index = cm3_type - 1;
254a2847172SGrzegorz Jaszczyk 		for (ap_idx = 0; ap_idx < ap_count; ap_idx++) {
255a2847172SGrzegorz Jaszczyk 			/* Check if we should load this image
256a2847172SGrzegorz Jaszczyk 			 * according to number of CPs
257a2847172SGrzegorz Jaszczyk 			 */
258a2847172SGrzegorz Jaszczyk 			if (bl2_plat_get_cp_count(ap_idx) <= cp_index) {
259a2847172SGrzegorz Jaszczyk 				NOTICE("Skipping MSS CP%d related image\n",
260a2847172SGrzegorz Jaszczyk 				       cp_index);
261a2847172SGrzegorz Jaszczyk 				break;
262a2847172SGrzegorz Jaszczyk 			}
263a2847172SGrzegorz Jaszczyk 
264a2847172SGrzegorz Jaszczyk 			NOTICE("Load image to CP%d MSS AP%d\n",
265a2847172SGrzegorz Jaszczyk 			       cp_index, ap_idx);
266a2847172SGrzegorz Jaszczyk 			ret = mss_image_load(single_img, image_size,
267a2847172SGrzegorz Jaszczyk 					     bl2_plat_get_cp_mss_regs(
26857870747SKonstantin Porotchkin 						     ap_idx, cp_index),
26957870747SKonstantin Porotchkin 					     bl2_plat_get_cp_mss_sram(
270a2847172SGrzegorz Jaszczyk 						     ap_idx, cp_index));
271a2847172SGrzegorz Jaszczyk 			if (ret != 0) {
272a2847172SGrzegorz Jaszczyk 				ERROR("SCP Image load failed\n");
273a2847172SGrzegorz Jaszczyk 				return -1;
274a2847172SGrzegorz Jaszczyk 			}
275a2847172SGrzegorz Jaszczyk 		}
276a2847172SGrzegorz Jaszczyk 		break;
277a2847172SGrzegorz Jaszczyk 	case MG_CP0:
278a2847172SGrzegorz Jaszczyk 	case MG_CP1:
279a2847172SGrzegorz Jaszczyk 	case MG_CP2:
280a2847172SGrzegorz Jaszczyk 		cp_index = cm3_type - MG_CP0;
281a2847172SGrzegorz Jaszczyk 		if (bl2_plat_get_cp_count(0) <= cp_index) {
282a2847172SGrzegorz Jaszczyk 			NOTICE("Skipping MG CP%d related image\n",
283a2847172SGrzegorz Jaszczyk 			       cp_index);
284a2847172SGrzegorz Jaszczyk 			break;
285a2847172SGrzegorz Jaszczyk 		}
286a2847172SGrzegorz Jaszczyk 		NOTICE("Load image to CP%d MG\n", cp_index);
2870081cdd1SGrzegorz Jaszczyk 		ret = mg_image_load(single_img, image_size, cp_index);
288a2847172SGrzegorz Jaszczyk 		if (ret != 0) {
289a2847172SGrzegorz Jaszczyk 			ERROR("SCP Image load failed\n");
290a2847172SGrzegorz Jaszczyk 			return -1;
291a2847172SGrzegorz Jaszczyk 		}
292a2847172SGrzegorz Jaszczyk 		break;
293a2847172SGrzegorz Jaszczyk 	default:
294a2847172SGrzegorz Jaszczyk 		ERROR("SCP_BL2 wrong img format (cm3_type=%d)\n", cm3_type);
295a2847172SGrzegorz Jaszczyk 		break;
296a2847172SGrzegorz Jaszczyk 	}
297a2847172SGrzegorz Jaszczyk 
298a2847172SGrzegorz Jaszczyk 	return 0;
299a2847172SGrzegorz Jaszczyk }
300a2847172SGrzegorz Jaszczyk 
301a2847172SGrzegorz Jaszczyk /* The Armada 8K has 5 service CPUs and Armada 7K has 3. Therefore it was
302a2847172SGrzegorz Jaszczyk  * required to provide a method for loading firmware to all of the service CPUs.
303a2847172SGrzegorz Jaszczyk  * To achieve that, the scp_bl2 image in fact is file containing up to 5
304a2847172SGrzegorz Jaszczyk  * concatenated firmwares and this routine splits concatenated image into single
305a2847172SGrzegorz Jaszczyk  * images dedicated for appropriate service CPU and then load them.
306a2847172SGrzegorz Jaszczyk  */
split_and_load_bl2_image(void * image)307a2847172SGrzegorz Jaszczyk static int split_and_load_bl2_image(void *image)
308a2847172SGrzegorz Jaszczyk {
309a2847172SGrzegorz Jaszczyk 	file_header_t *file_hdr;
310a2847172SGrzegorz Jaszczyk 	img_header_t *img_hdr;
311a2847172SGrzegorz Jaszczyk 	uintptr_t single_img;
312a2847172SGrzegorz Jaszczyk 	int i;
313a2847172SGrzegorz Jaszczyk 
314a2847172SGrzegorz Jaszczyk 	file_hdr = (file_header_t *)image;
315a2847172SGrzegorz Jaszczyk 
316a2847172SGrzegorz Jaszczyk 	if (file_hdr->magic != FILE_MAGIC) {
317a2847172SGrzegorz Jaszczyk 		ERROR("SCP_BL2 wrong img format\n");
318a2847172SGrzegorz Jaszczyk 		return -1;
319a2847172SGrzegorz Jaszczyk 	}
320a2847172SGrzegorz Jaszczyk 
321a2847172SGrzegorz Jaszczyk 	if (file_hdr->nr_of_imgs > MAX_NR_OF_FILES) {
322a2847172SGrzegorz Jaszczyk 		ERROR("SCP_BL2 concatenated image contains too many images\n");
323a2847172SGrzegorz Jaszczyk 		return -1;
324a2847172SGrzegorz Jaszczyk 	}
325a2847172SGrzegorz Jaszczyk 
326a2847172SGrzegorz Jaszczyk 	img_hdr = (img_header_t *)((uintptr_t)image + sizeof(file_header_t));
327a2847172SGrzegorz Jaszczyk 	single_img = (uintptr_t)image + sizeof(file_header_t) +
328a2847172SGrzegorz Jaszczyk 				    sizeof(img_header_t) * file_hdr->nr_of_imgs;
329a2847172SGrzegorz Jaszczyk 
330a2847172SGrzegorz Jaszczyk 	NOTICE("SCP_BL2 contains %d concatenated images\n",
331a2847172SGrzegorz Jaszczyk 							  file_hdr->nr_of_imgs);
332a2847172SGrzegorz Jaszczyk 	for (i = 0; i < file_hdr->nr_of_imgs; i++) {
333a2847172SGrzegorz Jaszczyk 
334a2847172SGrzegorz Jaszczyk 		/* Before loading make sanity check on header */
335a2847172SGrzegorz Jaszczyk 		if (img_hdr->version != HEADER_VERSION) {
336a2847172SGrzegorz Jaszczyk 			ERROR("Wrong header, img corrupted exiting\n");
337a2847172SGrzegorz Jaszczyk 			return -1;
338a2847172SGrzegorz Jaszczyk 		}
339a2847172SGrzegorz Jaszczyk 
340a2847172SGrzegorz Jaszczyk 		load_img_to_cm3(img_hdr->type, single_img, img_hdr->length);
341a2847172SGrzegorz Jaszczyk 
342a2847172SGrzegorz Jaszczyk 		/* Prepare offsets for next run */
343a2847172SGrzegorz Jaszczyk 		single_img += img_hdr->length;
344a2847172SGrzegorz Jaszczyk 		img_hdr++;
345a2847172SGrzegorz Jaszczyk 	}
346a2847172SGrzegorz Jaszczyk 
347a2847172SGrzegorz Jaszczyk 	return 0;
348a2847172SGrzegorz Jaszczyk }
349a2847172SGrzegorz Jaszczyk 
scp_bootloader_transfer(void * image,unsigned int image_size)350a2847172SGrzegorz Jaszczyk int scp_bootloader_transfer(void *image, unsigned int image_size)
351a2847172SGrzegorz Jaszczyk {
352a2847172SGrzegorz Jaszczyk #ifdef SCP_BL2_BASE
353a2847172SGrzegorz Jaszczyk 	assert((uintptr_t) image == SCP_BL2_BASE);
354a2847172SGrzegorz Jaszczyk #endif
355a2847172SGrzegorz Jaszczyk 
356a2847172SGrzegorz Jaszczyk 	VERBOSE("Concatenated img size %d\n", image_size);
357a2847172SGrzegorz Jaszczyk 
358a2847172SGrzegorz Jaszczyk 	if (image_size == 0) {
359a2847172SGrzegorz Jaszczyk 		ERROR("SCP_BL2 image size can't be 0 (current size = 0x%x)\n",
360a2847172SGrzegorz Jaszczyk 								    image_size);
361a2847172SGrzegorz Jaszczyk 		return -1;
362a2847172SGrzegorz Jaszczyk 	}
363a2847172SGrzegorz Jaszczyk 
364a2847172SGrzegorz Jaszczyk 	if (split_and_load_bl2_image(image))
365a2847172SGrzegorz Jaszczyk 		return -1;
366a2847172SGrzegorz Jaszczyk 
367a2847172SGrzegorz Jaszczyk 	return 0;
368a2847172SGrzegorz Jaszczyk }
369