/* * Copyright (c) 2017-2020, Broadcom * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "m0_cfg.h" #include "m0_ipc.h" #ifdef BCM_ELOG static void prepare_elog(void) { #if (CLEAN_DDR && !defined(MMU_DISABLED)) /* * Now DDR has been initialized. We want to copy all the logs in SRAM * into DDR so we will have much more space to store the logs in the * next boot stage */ bcm_elog_copy_log((void *)BCM_ELOG_BL31_BASE, MIN(BCM_ELOG_BL2_SIZE, BCM_ELOG_BL31_SIZE) ); /* * We are almost at the end of BL2, and we can stop log here so we do * not need to add 'bcm_elog_exit' to the standard BL2 code. The * benefit of capturing BL2 logs after this is very minimal in a * production system. */ bcm_elog_exit(); #endif /* * Notify CRMU that now it should pull logs from DDR instead of from * FS4 SRAM. */ SCP_WRITE_CFG(flash_log.can_use_ddr, 1); } #endif bool is_crmu_alive(void) { return (scp_send_cmd(MCU_IPC_MCU_CMD_NOP, 0, SCP_CMD_DEFAULT_TIMEOUT_US) == 0); } bool bcm_scp_issue_sys_reset(void) { return (scp_send_cmd(MCU_IPC_MCU_CMD_L1_RESET, 0, SCP_CMD_DEFAULT_TIMEOUT_US)); } /* * Note that this is just a temporary implementation until * channels are introduced */ int plat_bcm_bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info) { int scp_patch_activated, scp_patch_version; #ifndef EMULATION_SETUP uint8_t active_ch_bitmap, i; #endif uint32_t reset_state = 0; uint32_t mcu_ap_init_param = 0; /* * First check if SCP patch has already been loaded * Send NOP command and see if there is a valid response */ scp_patch_activated = (scp_send_cmd(MCU_IPC_MCU_CMD_NOP, 0, SCP_CMD_DEFAULT_TIMEOUT_US) == 0); if (scp_patch_activated) { INFO("SCP Patch is already active.\n"); reset_state = SCP_READ_CFG(board_cfg.reset_state); mcu_ap_init_param = SCP_READ_CFG(board_cfg.mcu_init_param); /* Clear reset state, it's been already read */ SCP_WRITE_CFG(board_cfg.reset_state, 0); if (mcu_ap_init_param & MCU_PATCH_LOADED_BY_NITRO) { /* * Reset "MCU_PATCH_LOADED_BY_NITRO" flag, but * Preserve any other flags we don't deal with here */ INFO("AP booted by Nitro\n"); SCP_WRITE_CFG( board_cfg.mcu_init_param, mcu_ap_init_param & ~MCU_PATCH_LOADED_BY_NITRO ); } } else { /* * MCU Patch not loaded, so load it. * MCU patch stamps critical points in REG9 (debug test-point) * Display its last content here. This helps to locate * where crash occurred if a CRMU watchdog kicked in. */ int ret; INFO("MCU Patch Point: 0x%x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG9)); ret = download_scp_patch((void *)scp_bl2_image_info->image_base, scp_bl2_image_info->image_size); if (ret != 0) return ret; VERBOSE("SCP Patch loaded OK.\n"); ret = scp_send_cmd(MCU_IPC_MCU_CMD_INIT, MCU_PATCH_LOADED_BY_AP, SCP_CMD_SCP_BOOT_TIMEOUT_US); if (ret) { ERROR("SCP Patch could not initialize; error %d\n", ret); return ret; } INFO("SCP Patch successfully initialized.\n"); } scp_patch_version = scp_send_cmd(MCU_IPC_MCU_CMD_GET_FW_VERSION, 0, SCP_CMD_DEFAULT_TIMEOUT_US); INFO("SCP Patch version :0x%x\n", scp_patch_version); /* Next block just reports current AVS voltages (if applicable) */ { uint16_t vcore_mv, ihost03_mv, ihost12_mv; vcore_mv = SCP_READ_CFG16(vcore.millivolts) + SCP_READ_CFG8(vcore.avs_cfg.additive_margin); ihost03_mv = SCP_READ_CFG16(ihost03.millivolts) + SCP_READ_CFG8(ihost03.avs_cfg.additive_margin); ihost12_mv = SCP_READ_CFG16(ihost12.millivolts) + SCP_READ_CFG8(ihost12.avs_cfg.additive_margin); if (vcore_mv || ihost03_mv || ihost12_mv) { INFO("AVS voltages from cfg (including margin)\n"); if (vcore_mv > 0) INFO("%s\tVCORE: %dmv\n", SCP_READ_CFG8(vcore.avs_cfg.avs_set) ? "*" : "n/a", vcore_mv); if (ihost03_mv > 0) INFO("%s\tIHOST03: %dmv\n", SCP_READ_CFG8(ihost03.avs_cfg.avs_set) ? "*" : "n/a", ihost03_mv); if (ihost12_mv > 0) INFO("%s\tIHOST12: %dmv\n", SCP_READ_CFG8(ihost12.avs_cfg.avs_set) ? "*" : "n/a", ihost12_mv); } else { INFO("AVS settings not applicable\n"); } } #if (CLEAN_DDR && !defined(MMU_DISABLED) && !defined(EMULATION_SETUP)) /* This will clean the DDR and enable ECC if set */ check_ddr_clean(); #endif #if (WARMBOOT_DDR_S3_SUPPORT && ELOG_STORE_MEDIA_DDR) elog_init_ddr_log(); #endif #ifdef BCM_ELOG /* Prepare ELOG to use DDR */ prepare_elog(); #endif #ifndef EMULATION_SETUP /* Ask ddr_init to save obtained DDR information into DDR */ ddr_info_save(); #endif /* * Configure TMON DDR address. * This cfg is common for all cases */ SCP_WRITE_CFG(tmon_cfg.ddr_desc, TMON_SHARED_DDR_ADDRESS); if (reset_state == SOFT_RESET_L3 && !mcu_ap_init_param) { INFO("SCP configuration after L3 RESET done.\n"); return 0; } if (bcm_chimp_is_nic_mode()) /* Configure AP WDT to not reset the NIC interface */ SCP_WRITE_CFG(board_cfg.apwdt_reset_type, SOFT_RESET_L3); #if (WARMBOOT_DDR_S3_SUPPORT && ELOG_STORE_MEDIA_DDR) /* When AP WDog triggers perform L3 reset if DDR err logging enabled */ SCP_WRITE_CFG(board_cfg.apwdt_reset_type, SOFT_RESET_L3); #endif #ifndef EMULATION_SETUP #ifdef DDR_SCRUB_ENA ddr_scrub_enable(); #endif /* Fill the Active channel information */ active_ch_bitmap = get_active_ddr_channel(); for (i = 0; i < MAX_NR_DDR_CH; i++) SCP_WRITE_CFG(ddr_cfg.ddr_cfg[i], (active_ch_bitmap & BIT(i)) ? 1 : 0); #endif return 0; }