spm.c
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 3 #include <assert.h> 4 #include <console/console.h> 5 #include <delay.h> 6 #include <soc/mcu_common.h> 7 #include <soc/spm.h> 8 #include <soc/symbols.h> 9 #include <string.h> 10 #include <timer.h> 11 12 #define SPMFW_HEADER_SIZE 16 13 14 __weak void spm_extern_initialize(void) { /* do nothing */ } 15 16 static void spm_set_sysclk_settle(void) 17 { 18 write32(&mtk_spm->spm_clk_settle, SPM_SYSCLK_SETTLE); 19 } 20 21 static void spm_kick_im_to_fetch(const struct dyna_load_pcm *pcm) 22 { 23 uintptr_t ptr; 24 u32 dmem_words; 25 u32 pmem_words; 26 u32 total_words; 27 u32 pmem_start; 28 u32 dmem_start; 29 30 ptr = (uintptr_t)pcm->buf + SPM_SYSTEM_BASE_OFFSET; 31 pmem_words = pcm->desc.pmem_words; 32 total_words = pcm->desc.total_words; 33 dmem_words = total_words - pmem_words; 34 pmem_start = pcm->desc.pmem_start; 35 dmem_start = pcm->desc.dmem_start; 36 37 printk(BIOS_DEBUG, "%s: ptr = %#lx, pmem/dmem words = %#x/%#x\n", 38 __func__, (long)ptr, pmem_words, dmem_words); 39 40 /* DMA needs 16-byte aligned source data. */ 41 assert(ptr % 16 == 0); 42 43 write32(&mtk_spm->md32pcm_dma0_src, ptr); 44 write32(&mtk_spm->md32pcm_dma0_dst, pmem_start); 45 write32(&mtk_spm->md32pcm_dma0_wppt, pmem_words); 46 write32(&mtk_spm->md32pcm_dma0_wpto, dmem_start); 47 write32(&mtk_spm->md32pcm_dma0_count, total_words); 48 write32(&mtk_spm->md32pcm_dma0_con, MD32PCM_DMA0_CON_VAL); 49 write32(&mtk_spm->md32pcm_dma0_start, MD32PCM_DMA0_START_VAL); 50 51 setbits32(&mtk_spm->pcm_con0, SPM_REGWR_CFG_KEY | PCM_CK_EN_LSB); 52 } 53 54 void spm_set_pcm_flags(const struct pwr_ctrl *pwrctrl) 55 { 56 u32 pcm_flags = pwrctrl->pcm_flags, pcm_flags1 = pwrctrl->pcm_flags1; 57 58 /* Set PCM flags and data */ 59 if (pwrctrl->pcm_flags_cust_clr != 0) 60 pcm_flags &= ~pwrctrl->pcm_flags_cust_clr; 61 if (pwrctrl->pcm_flags_cust_set != 0) 62 pcm_flags |= pwrctrl->pcm_flags_cust_set; 63 if (pwrctrl->pcm_flags1_cust_clr != 0) 64 pcm_flags1 &= ~pwrctrl->pcm_flags1_cust_clr; 65 if (pwrctrl->pcm_flags1_cust_set != 0) 66 pcm_flags1 |= pwrctrl->pcm_flags1_cust_set; 67 68 write32(&mtk_spm->spm_sw_flag_0, pcm_flags); 69 write32(&mtk_spm->spm_sw_flag_1, pcm_flags1); 70 write32(&mtk_spm->spm_sw_rsv[7], pcm_flags); 71 write32(&mtk_spm->spm_sw_rsv[8], pcm_flags1); 72 } 73 74 static void spm_parse_firmware(struct mtk_mcu *mcu) 75 { 76 size_t file_size, copy_size; 77 int offset; 78 u16 firmware_size; 79 80 struct dyna_load_pcm *pcm = (struct dyna_load_pcm *)mcu->priv; 81 file_size = mcu->run_size; 82 83 /* 84 * spmfw layout: 85 * u16 firmware_size 86 * u32 binary[firmware_size] 87 * struct pcm_desc descriptor 88 * char *version 89 */ 90 91 /* Firmware size */ 92 offset = 0; 93 copy_size = sizeof(firmware_size); 94 memcpy(&firmware_size, mcu->load_buffer + offset, copy_size); 95 printk(BIOS_DEBUG, "SPM: binary array size = %#x\n", firmware_size); 96 97 /* Binary */ 98 offset = SPMFW_HEADER_SIZE; /* binary start offset */ 99 copy_size = firmware_size * sizeof(u32); 100 assert(offset < file_size); 101 pcm->buf = (u8 *)(mcu->load_buffer + offset); 102 103 /* Descriptor */ 104 offset += copy_size; 105 assert(offset < file_size); 106 copy_size = sizeof(pcm->desc); 107 memcpy(&pcm->desc, mcu->load_buffer + offset, copy_size); 108 109 /* Firmware size and total words need to be the same */ 110 assert(firmware_size == pcm->desc.total_words); 111 112 /* Version */ 113 offset += copy_size; 114 assert(offset < file_size); 115 printk(BIOS_INFO, "SPM: spmfw (version %.*s)\n", 116 (int)(file_size - offset), 117 (u8 *)mcu->load_buffer + offset); 118 } 119 120 static void reset_spm(struct mtk_mcu *mcu) 121 { 122 struct dyna_load_pcm *pcm = (struct dyna_load_pcm *)mcu->priv; 123 const struct pwr_ctrl *spm_init_ctrl = get_pwr_ctrl(); 124 125 spm_parse_firmware(mcu); 126 spm_reset_and_init_pcm(); 127 spm_kick_im_to_fetch(pcm); 128 spm_init_pcm_register(); 129 spm_set_wakeup_event(spm_init_ctrl); 130 spm_kick_pcm_to_run(spm_init_ctrl); 131 } 132 133 static struct mtk_mcu spm = { 134 .firmware_name = CONFIG_SPM_FIRMWARE, 135 .reset = reset_spm, 136 }; 137 138 void spm_code_swapping(void) 139 { 140 u32 mask; 141 142 mask = read32(&mtk_spm->spm_wakeup_event_mask); 143 write32(&mtk_spm->spm_wakeup_event_mask, 144 mask & ~SPM_WAKEUP_EVENT_MASK_BIT0); 145 write32(&mtk_spm->spm_cpu_wakeup_event, 1); 146 write32(&mtk_spm->spm_cpu_wakeup_event, 0); 147 write32(&mtk_spm->spm_wakeup_event_mask, mask); 148 } 149 150 int spm_init(void) 151 { 152 struct dyna_load_pcm pcm; 153 struct stopwatch sw; 154 const struct pwr_ctrl *spm_init_ctrl = get_pwr_ctrl(); 155 156 stopwatch_init(&sw); 157 158 spm_register_init(); 159 spm_set_power_control(spm_init_ctrl); 160 spm_set_sysclk_settle(); 161 spm_extern_initialize(); 162 163 spm.load_buffer = _dram_dma; 164 spm.buffer_size = REGION_SIZE(dram_dma); 165 spm.priv = (void *)&pcm; 166 167 if (mtk_init_mcu(&spm)) { 168 printk(BIOS_ERR, "SPM: %s: failed in mtk_init_mcu\n", __func__); 169 return -1; 170 } 171 172 printk(BIOS_INFO, "SPM: %s done in %lld msecs, spm pc = %#x\n", 173 __func__, stopwatch_duration_msecs(&sw), 174 read32(&mtk_spm->md32pcm_pc)); 175 176 return 0; 177 }