/* * Copyright (C) 2018 Marvell International Ltd. * * SPDX-License-Identifier: BSD-3-Clause * https://spdx.org/licenses */ #include #include #include #include #include #include #include #include #include #include #include #include #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 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); /* Wipe the MSS SRAM after using it as copy buffer */ if (sram) { memset((void *)sram, 0, MSS_SRAM_SIZE); } /* 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; }