/ src / soc / mediatek / common / spm.c
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  }