/* SPDX-License-Identifier: GPL-2.0-only */ #include <assert.h> #include <cbfs.h> #include <console/console.h> #include <device/mmio.h> #include <endian.h> #include <soc/emi.h> #include <soc/spm.h> #include <timer.h> #define BUF_SIZE (16 * KiB) static uint8_t spm_bin[BUF_SIZE] __aligned(8); static int spm_register_init(void) { u32 pcm_fsm_sta; write32(&mtk_spm->poweron_config_set, SPM_REGWR_CFG_KEY | BCLK_CG_EN_LSB | MD_BCLK_CG_EN_LSB); write32(&mtk_spm->spm_power_on_val1, POWER_ON_VAL1_DEF); write32(&mtk_spm->pcm_pwr_io_en, 0); write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB | PCM_SW_RESET_LSB); write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB); pcm_fsm_sta = read32(&mtk_spm->pcm_fsm_sta); if ((pcm_fsm_sta & PCM_FSM_STA_MASK) != PCM_FSM_STA_DEF) { printk(BIOS_ERR, "PCM reset failed\n"); return -1; } write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB | EN_IM_SLEEP_DVS_LSB); write32(&mtk_spm->pcm_con1, SPM_REGWR_CFG_KEY | EVENT_LOCK_EN_LSB | SPM_SRAM_ISOINT_B_LSB | MIF_APBEN_LSB | SCP_APB_INTERNAL_EN_LSB); write32(&mtk_spm->pcm_im_ptr, 0); write32(&mtk_spm->pcm_im_len, 0); write32(&mtk_spm->spm_clk_con, read32(&mtk_spm->spm_clk_con) | SYSCLK1_EN_CTRL | SPM_LOCK_INFRA_DCM_LSB | EXT_SRCCLKEN_MASK | CXO32K_REMOVE_EN_MD1_LSB | CLKSQ1_SEL_CTRL_LSB | SRCLKEN0_EN_LSB | SYSCLK1_SRC_MASK_B); write32(&mtk_spm->spm_wakeup_event_mask, SPM_WAKEUP_EVENT_MASK_DEF); write32(&mtk_spm->spm_irq_mask, ISRM_ALL); write32(&mtk_spm->spm_irq_sta, ISRC_ALL); write32(&mtk_spm->spm_swint_clr, PCM_SW_INT_ALL); write32(&mtk_spm->pcm_reg_data_ini, read32(&mtk_spm->spm_power_on_val1)); write32(&mtk_spm->pcm_pwr_io_en, PCM_RF_SYNC_R7); write32(&mtk_spm->pcm_pwr_io_en, 0); write32(&mtk_spm->ddr_en_dbc_len, MD_DDR_EN_0_DBC_LEN | MD_DDR_EN_1_DBC_LEN | CONN_DDR_EN_DBC_LEN); clrsetbits32(&mtk_spm->spare_ack_mask, SPARE_ACK_MASK_B_BIT1, SPARE_ACK_MASK_B_BIT0); write32(&mtk_spm->sysrom_con, IFR_SRAMROM_ROM_PDN); write32(&mtk_spm->spm_pc_trace_con, SPM_PC_TRACE_OFFSET | SPM_PC_TRACE_HW_EN_LSB); setbits32(&mtk_spm->spare_src_req_mask, SPARE1_DDREN_MASK_B_LSB); return 0; } static int spm_code_swapping(void) { u32 con1; con1 = read32(&mtk_spm->spm_wakeup_event_mask); write32(&mtk_spm->spm_wakeup_event_mask, con1 & ~WAKEUP_EVENT_MASK_B_BIT0); write32(&mtk_spm->spm_cpu_wakeup_event, 1); if (!wait_us(SPM_CORE_TIMEOUT, read32(&mtk_spm->spm_irq_sta) & PCM_IRQ_ROOT_MASK_LSB)) { printk(BIOS_ERR, "timeout: r15=%#x, pcmsta=%#x, irqsta=%#x [%d]\n", read32(&mtk_spm->pcm_reg15_data), read32(&mtk_spm->pcm_fsm_sta), read32(&mtk_spm->spm_irq_sta), SPM_CORE_TIMEOUT); return -1; } write32(&mtk_spm->spm_cpu_wakeup_event, 0); write32(&mtk_spm->spm_wakeup_event_mask, con1); return 0; } static int spm_reset_and_init_pcm(const struct pcm_desc *pcmdesc) { u32 con1, pcm_fsm_sta; if (read32(&mtk_spm->pcm_reg1_data) == SPM_PCM_REG1_DATA_CHECK && read32(&mtk_spm->pcm_reg15_data) != SPM_PCM_REG15_DATA_CHECK) { if (spm_code_swapping()) return -1; write32(&mtk_spm->spm_power_on_val0, read32(&mtk_spm->pcm_reg0_data)); } write32(&mtk_spm->pcm_pwr_io_en, 0); clrsetbits32(&mtk_spm->pcm_con1, PCM_TIMER_EN_LSB, SPM_REGWR_CFG_KEY); write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB | PCM_SW_RESET_LSB); write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB); pcm_fsm_sta = read32(&mtk_spm->pcm_fsm_sta); if ((pcm_fsm_sta & PCM_FSM_STA_MASK) != PCM_FSM_STA_DEF) { printk(BIOS_ERR, "reset pcm(PCM_FSM_STA=%#x)\n", read32(&mtk_spm->pcm_fsm_sta)); return -1; } write32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB | EN_IM_SLEEP_DVS_LSB); con1 = read32(&mtk_spm->pcm_con1) & PCM_WDT_WAKE_MODE_LSB; write32(&mtk_spm->pcm_con1, con1 | SPM_REGWR_CFG_KEY | EVENT_LOCK_EN_LSB | SPM_SRAM_ISOINT_B_LSB | (pcmdesc->replace ? 0 : IM_NONRP_EN_LSB) | MIF_APBEN_LSB | SCP_APB_INTERNAL_EN_LSB); return 0; } static void spm_load_pcm_code(const struct dyna_load_pcm *pcm) { int i; write32(&mtk_spm->pcm_con1, read32(&mtk_spm->pcm_con1) | SPM_REGWR_CFG_KEY | IM_SLAVE_LSB); for (i = 0; i < pcm->desc.size; i++) { write32(&mtk_spm->pcm_im_host_rw_ptr, PCM_IM_HOST_EN_LSB | PCM_IM_HOST_W_EN_LSB | i); write32(&mtk_spm->pcm_im_host_rw_dat, (u32)*(pcm->buf + i)); } write32(&mtk_spm->pcm_im_host_rw_ptr, 0); } static void spm_check_pcm_code(const struct dyna_load_pcm *pcm) { int i; for (i = 0; i < pcm->desc.size; i++) { write32(&mtk_spm->pcm_im_host_rw_ptr, PCM_IM_HOST_EN_LSB | i); if ((read32(&mtk_spm->pcm_im_host_rw_dat)) != (u32)*(pcm->buf + i)) spm_load_pcm_code(pcm); } write32(&mtk_spm->pcm_im_host_rw_ptr, 0); } static void spm_kick_im_to_fetch(const struct dyna_load_pcm *pcm) { u32 con0; spm_load_pcm_code(pcm); spm_check_pcm_code(pcm); printk(BIOS_DEBUG, "%s: ptr = %p\n", __func__, pcm->buf); printk(BIOS_DEBUG, "%s: len = %d\n", __func__, pcm->desc.size); con0 = read32(&mtk_spm->pcm_con0) & ~(IM_KICK_L_LSB | PCM_KICK_L_LSB); write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB | IM_KICK_L_LSB); write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB); } static void spm_init_pcm_register(void) { write32(&mtk_spm->pcm_reg_data_ini, read32(&mtk_spm->spm_power_on_val0)); write32(&mtk_spm->pcm_pwr_io_en, PCM_RF_SYNC_R0); write32(&mtk_spm->pcm_pwr_io_en, 0); write32(&mtk_spm->pcm_reg_data_ini, read32(&mtk_spm->spm_power_on_val1)); write32(&mtk_spm->pcm_pwr_io_en, PCM_RF_SYNC_R7); write32(&mtk_spm->pcm_pwr_io_en, 0); } static void spm_init_event_vector(const struct pcm_desc *pcmdesc) { for (int i = 0; i < PCM_EVENT_VECTOR_NUM; i++) write32(&mtk_spm->pcm_event_vector[i], pcmdesc->vector[i]); } static const char * const dyna_load_pcm_path[] = { [DYNA_LOAD_PCM_SUSPEND_LP4_3733] = "pcm_allinone_lp4_3733.bin", [DYNA_LOAD_PCM_SUSPEND_LP4_3200] = "pcm_allinone_lp4_3200.bin", }; static int spm_load_firmware(enum dyna_load_pcm_index index, struct dyna_load_pcm *pcm) { /* * Layout: * u16 firmware_size * u32 binary[firmware_size] * struct pcm_desc descriptor * char *version */ u16 firmware_size; int copy_size; const char *file_name = dyna_load_pcm_path[index]; struct stopwatch sw; stopwatch_init(&sw); size_t file_size = cbfs_load(file_name, spm_bin, sizeof(spm_bin)); if (file_size == 0) { printk(BIOS_ERR, "SPM binary %s not found\n", file_name); return -1; } int offset = 0; /* firmware size */ copy_size = sizeof(firmware_size); memcpy(&firmware_size, spm_bin + offset, copy_size); printk(BIOS_DEBUG, "SPM: binary array size = %d\n", firmware_size); offset += copy_size; /* binary */ assert(offset < file_size); copy_size = firmware_size * 4; pcm->buf = (u32 *)(spm_bin + offset); offset += copy_size; /* descriptor */ assert(offset < file_size); copy_size = sizeof(struct pcm_desc); memcpy((void *)&(pcm->desc.size), spm_bin + offset, copy_size); offset += copy_size; /* version */ /* The terminating character should be contained in the spm binary */ assert(spm_bin[file_size - 1] == '\0'); assert(offset < file_size); printk(BIOS_DEBUG, "SPM: version = %s\n", spm_bin + offset); printk(BIOS_INFO, "SPM binary loaded in %lld msecs\n", stopwatch_duration_msecs(&sw)); return 0; } static void spm_kick_pcm_to_run(void) { uint32_t con0; write32(&mtk_spm->spm_mas_pause_mask_b, SPM_MAS_PAUSE_MASK_B_VAL); write32(&mtk_spm->spm_mas_pause2_mask_b, SPM_MAS_PAUSE2_MASK_B_VAL); write32(&mtk_spm->pcm_reg_data_ini, 0); write32(&mtk_spm->pcm_pwr_io_en, PCM_PWRIO_EN_R0 | PCM_PWRIO_EN_R7); printk(BIOS_DEBUG, "SPM: %s\n", __func__); /* check IM ready */ while ((read32(&mtk_spm->pcm_fsm_sta) & IM_STATE_MASK) != IM_STATE) ; /* kick PCM to run, and toggle PCM_KICK */ con0 = read32(&mtk_spm->pcm_con0) & ~(IM_KICK_L_LSB | PCM_KICK_L_LSB); write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB | PCM_KICK_L_LSB); write32(&mtk_spm->pcm_con0, con0 | SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB); printk(BIOS_DEBUG, "SPM: %s done\n", __func__); } int spm_init(void) { struct pcm_desc *pcmdesc; enum dyna_load_pcm_index index; struct stopwatch sw; stopwatch_init(&sw); if (CONFIG(MT8183_DRAM_EMCP)) index = DYNA_LOAD_PCM_SUSPEND_LP4_3733; else index = DYNA_LOAD_PCM_SUSPEND_LP4_3200; printk(BIOS_DEBUG, "SPM: pcm index = %d\n", index); struct dyna_load_pcm pcm; if (spm_load_firmware(index, &pcm)) { printk(BIOS_ERR, "SPM: firmware is not ready\n"); printk(BIOS_ERR, "SPM: check dram type and firmware version\n"); return -1; } pcmdesc = &pcm.desc; if (spm_register_init()) return -1; if (spm_reset_and_init_pcm(pcmdesc)) return -1; spm_kick_im_to_fetch(&pcm); spm_init_pcm_register(); spm_init_event_vector(pcmdesc); spm_kick_pcm_to_run(); printk(BIOS_INFO, "SPM: %s done in %lld msecs\n", __func__, stopwatch_duration_msecs(&sw)); return 0; }