/* * 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 /* timer functionality */ #include "mss_scp_bootloader.h" /* MSS windows configuration */ #define MSS_AEBR(base) (base + 0x160) #define MSS_AIBR(base) (base + 0x164) #define MSS_AEBR_MASK 0xFFF #define MSS_AIBR_MASK 0xFFF #define MSS_EXTERNAL_SPACE 0x50000000 #define MSS_EXTERNAL_ACCESS_BIT 28 #define MSS_EXTERNAL_ADDR_MASK 0xfffffff #define MSS_INTERNAL_ACCESS_BIT 28 struct addr_map_win ccu_mem_map[] = { {MVEBU_CP_REGS_BASE(0), 0x4000000, IO_0_TID} }; /* Since the scp_bl2 image can contain firmware for cp1 and cp0 coprocessors, * the access to cp0 and cp1 need to be provided. More precisely it is * required to: * - get the information about device id which is stored in CP0 registers * (to distinguish between cases where we have cp0 and cp1 or standalone cp0) * - get the access to cp which is needed for loading fw for cp0/cp1 * coprocessors * This function configures ccu windows accordingly. * * Note: there is no need to restore previous ccu configuration, since in next * phase (BL31) the init_ccu will be called (via apn806_init/ * bl31_plat_arch_setu) and therefore the ccu configuration will be overwritten. */ static int bl2_plat_mmap_init(void) { int cfg_num, win_id, cfg_idx, cp; cfg_num = ARRAY_SIZE(ccu_mem_map); /* CCU window-0 should not be counted - it's already used */ if (cfg_num > (MVEBU_CCU_MAX_WINS - 1)) { ERROR("BL2: %s: trying to open too many windows\n", __func__); return -1; } /* Enable required CCU windows * Do not touch CCU window 0, * it's used for the internal registers access */ for (cfg_idx = 0, win_id = 1; (win_id < MVEBU_CCU_MAX_WINS) && (cfg_idx < cfg_num); win_id++) { /* Skip already enabled CCU windows */ if (ccu_is_win_enabled(MVEBU_AP0, win_id)) continue; /* Enable required CCU windows */ ccu_win_check(&ccu_mem_map[cfg_idx]); ccu_enable_win(MVEBU_AP0, &ccu_mem_map[cfg_idx], win_id); cfg_idx++; } /* Config address for each cp other than cp0 */ for (cp = 1; cp < CP_COUNT; cp++) update_cp110_default_win(cp); /* There is need to configure IO_WIN windows again to overwrite * temporary configuration done during update_cp110_default_win */ init_io_win(MVEBU_AP0); /* Open AMB bridge required for MG access */ for (cp = 0; cp < CP_COUNT; cp++) cp110_amb_init(MVEBU_CP_REGS_BASE(cp)); return 0; } /***************************************************************************** * Transfer SCP_BL2 from Trusted RAM using the SCP Download protocol. * Return 0 on success, -1 otherwise. ***************************************************************************** */ int bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info) { int ret; INFO("BL2: Initiating SCP_BL2 transfer to SCP\n"); /* initialize time (for delay functionality) */ plat_delay_timer_init(); ret = bl2_plat_mmap_init(); if (ret != 0) return ret; ret = scp_bootloader_transfer((void *)scp_bl2_image_info->image_base, scp_bl2_image_info->image_size); if (ret == 0) INFO("BL2: SCP_BL2 transferred to SCP\n"); else ERROR("BL2: SCP_BL2 transfer failure\n"); return ret; } uintptr_t bl2_plat_get_cp_mss_regs(int ap_idx, int cp_idx) { return MVEBU_CP_REGS_BASE(cp_idx) + 0x280000; } uintptr_t bl2_plat_get_ap_mss_regs(int ap_idx) { return MVEBU_REGS_BASE + 0x580000; } uint32_t bl2_plat_get_cp_count(int ap_idx) { uint32_t revision = cp110_device_id_get(MVEBU_CP_REGS_BASE(0)); /* A8040: two CPs. * A7040: one CP. */ if (revision == MVEBU_80X0_DEV_ID || revision == MVEBU_80X0_CP115_DEV_ID) return 2; else if (revision == MVEBU_CN9130_DEV_ID) return CP_COUNT; else return 1; } uint32_t bl2_plat_get_ap_count(void) { /* A8040 and A7040 have only one AP */ return 1; } void bl2_plat_configure_mss_windows(uintptr_t mss_regs) { /* set AXI External and Internal Address Bus extension */ mmio_write_32(MSS_AEBR(mss_regs), ((0x0 >> MSS_EXTERNAL_ACCESS_BIT) & MSS_AEBR_MASK)); mmio_write_32(MSS_AIBR(mss_regs), ((mss_regs >> MSS_INTERNAL_ACCESS_BIT) & MSS_AIBR_MASK)); }