1 /* 2 * Copyright (c) 2025, Mediatek Inc. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <inttypes.h> 9 #include <stddef.h> 10 #include <stdint.h> 11 #include <stdio.h> 12 #include <string.h> 13 14 #include <common/debug.h> 15 #include <lib/mmio.h> 16 #include <plat/common/platform.h> 17 18 #include <lib/pm/mtk_pm.h> 19 #include <lpm_v2/mt_lp_rqm.h> 20 #include <mt_spm.h> 21 #include <mt_spm_common.h> 22 #include <mt_spm_conservation.h> 23 #include <mt_spm_reg.h> 24 #include <mt_spm_vcorefs.h> 25 #include <platform_def.h> 26 #ifndef MTK_PLAT_SPM_UART_UNSUPPORT 27 #include <uart.h> 28 #endif 29 30 #define MT_RESUMETIME_THRESHOLD_MAX 5 /*ms*/ 31 #define IS_RESUME_OVERTIME(delta) (delta > MT_RESUMETIME_THRESHOLD_MAX) 32 33 static struct wake_status spm_wakesta; /* Record last wakesta */ 34 static wake_reason_t spm_wake_reason = WR_NONE; 35 static struct resource_req_status generic_spm_resource_req = { 36 .id = MT_LP_RQ_ID_ALL_USAGE, 37 .val = 0, 38 }; 39 40 #ifdef MT_SPM_TIMESTAMP_SUPPORT 41 struct spm_con_info { 42 uint32_t wakeup_obs; 43 unsigned short entries; 44 unsigned short resume; 45 uint64_t entry_time; 46 }; 47 static struct spm_con_info spm_cst_info; 48 #endif 49 50 #if SPM_FW_NO_RESUME 51 #define do_spm_init(pwrctrl) \ 52 ({ \ 53 int local_ret = 0; \ 54 local_ret; \ 55 }) 56 #define do_spm_run(pwrctrl) __spm_send_cpu_wakeup_event() 57 #else 58 #define do_spm_run(pwrctrl) __spm_kick_pcm_to_run(pwrctrl) 59 #define do_spm_init(pcmdesc) __spm_conservation_fw_init(pcmdesc) 60 #endif 61 62 #ifndef MTK_PLAT_SPM_TRACE_UNSUPPORT 63 #define SPM_CONSERVATION_BOUND (MT_SPM_TRACE_LP_RINGBUF_MAX - 1) 64 65 static const uint32_t spm_conservation_trace_comm_sz = 66 sizeof(struct wake_status_trace_comm); 67 68 static void spm_conservation_trace_lp(struct wake_status_trace *trace) 69 { 70 MT_SPM_TRACE_LP_RINGBUF(&trace->comm, spm_conservation_trace_comm_sz); 71 } 72 73 static void spm_conservation_trace_suspend(struct wake_status_trace *trace) 74 { 75 if (!trace) { 76 INFO("[%s:%d] Enter\n", __func__, __LINE__); 77 return; 78 } 79 } 80 81 #endif 82 83 #ifdef MT_SPM_TIMESTAMP_SUPPORT 84 int spm_conservation_wakeup_obs(int is_set, int cat, uint32_t wake_src_bits) 85 { 86 spm_lock_get(); 87 if (is_set) 88 spm_cst_info.wakeup_obs |= wake_src_bits; 89 else 90 spm_cst_info.wakeup_obs &= ~wake_src_bits; 91 spm_lock_release(); 92 return 0; 93 } 94 #endif 95 96 static int go_to_spm_before_wfi(int state_id, uint32_t ext_opand, 97 struct spm_lp_scen *spm_lp, 98 uint32_t resource_req) 99 { 100 int ret = 0; 101 struct pwr_ctrl *pwrctrl; 102 uint32_t cpu = plat_my_core_pos(); 103 104 pwrctrl = spm_lp->pwrctrl; 105 106 #if SPM_FW_NO_RESUME == 0 107 ret = do_spm_init(pwrctrl); 108 109 if (ret) 110 return ret; 111 #endif 112 __spm_set_cpu_status(cpu); 113 __spm_set_power_control(pwrctrl, resource_req); 114 __spm_set_wakeup_event(pwrctrl); 115 #if defined(CONFIG_MTK_VCOREDVFS_SUPPORT) 116 __spm_sync_vcore_dvfs_power_control(pwrctrl, __spm_vcorefs.pwrctrl); 117 #endif 118 119 __spm_set_pcm_flags(pwrctrl); 120 121 if (ext_opand & MT_SPM_EX_OP_CLR_26M_RECORD) 122 __spm_clean_before_wfi(); 123 124 if (ext_opand & MT_SPM_EX_OP_HW_S1_DETECT) 125 spm_hw_s1_state_monitor_resume(); 126 127 do_spm_run(pwrctrl); 128 129 return ret; 130 } 131 132 static void go_to_spm_after_wfi(int state_id, uint32_t ext_opand, 133 struct spm_lp_scen *spm_lp, 134 struct wake_status **status) 135 { 136 #ifdef MT_SPM_TIMESTAMP_SUPPORT 137 uint64_t ktime = 0; 138 #endif 139 uint32_t ext_status = 0; 140 141 spm_wakesta.tr.comm.resumetime = 0; 142 spm_wakesta.tr.comm.times_h = spm_wakesta.tr.comm.times_l = 0; 143 #ifdef MT_SPM_TIMESTAMP_SUPPORT 144 if (ext_opand & MT_SPM_EX_OP_TRACE_TIMESTAMP_EN) { 145 MT_SPM_TIME_GET(ktime); 146 spm_wakesta.tr.comm.times_h = (ktime >> 32); 147 spm_wakesta.tr.comm.times_l = (ktime & (uint32_t)-1); 148 } 149 #endif 150 151 if (ext_opand & MT_SPM_EX_OP_HW_S1_DETECT) 152 spm_hw_s1_state_monitor_pause(&ext_status); 153 154 __spm_ext_int_wakeup_req_clr(); 155 156 __spm_get_wakeup_status(&spm_wakesta, ext_status); 157 158 #ifdef MT_SPM_TIMESTAMP_SUPPORT 159 160 if (spm_wakesta.tr.comm.r12 & R12_SYS_CIRQ_IRQ_B) 161 INFO("[%s:%d] spm receive wakeup r12 = (0x%" PRIx32 162 ") (0x%" PRIx32 "%" PRIx32 ")\n", 163 __func__, __LINE__, spm_wakesta.tr.comm.r12, 164 mmio_read_32(SYS_TIMER_VALUE_H), 165 mmio_read_32(SYS_TIMER_VALUE_L)); 166 167 if (ext_opand & MT_SPM_EX_OP_TRACE_TIMESTAMP_EN) { 168 if ((ext_opand & MT_SPM_EX_OP_TIME_CHECK) && 169 (spm_cst_info.entries != spm_cst_info.resume)) { 170 uint64_t t_resume = 171 (ktime - spm_cst_info.entry_time) / 1000000; 172 uint64_t t_spm = spm_wakesta.tr.comm.timer_out >> 5; 173 174 spm_wakesta.tr.comm.resumetime = 175 (t_resume > t_spm) ? (t_resume - t_spm) : 0; 176 if ((t_resume > t_spm) && 177 IS_RESUME_OVERTIME( 178 spm_wakesta.tr.comm.resumetime)) { 179 INFO("[%s:%d] Overtime use %lu(ms)(%" PRIu64 180 ",%" PRIu64 ")\n", 181 __func__, __LINE__, (t_resume - t_spm), 182 t_resume, t_spm); 183 } 184 spm_cst_info.resume = spm_cst_info.entries; 185 } 186 } 187 #endif 188 189 if (status) 190 *status = &spm_wakesta; 191 192 #ifndef MTK_PLAT_SPM_TRACE_UNSUPPORT 193 if (ext_opand & MT_SPM_EX_OP_TRACE_LP) 194 spm_conservation_trace_lp(&spm_wakesta.tr); 195 else if (ext_opand & MT_SPM_EX_OP_TRACE_SUSPEND) 196 spm_conservation_trace_suspend(&spm_wakesta.tr); 197 #endif 198 __spm_clean_after_wakeup(); 199 spm_wake_reason = __spm_output_wake_reason(&spm_wakesta); 200 } 201 202 int spm_conservation(int state_id, uint32_t ext_opand, 203 struct spm_lp_scen *spm_lp, uint32_t resource_req) 204 { 205 uint32_t rc_state = resource_req; 206 207 if (!spm_lp) 208 return -1; 209 210 spm_lock_get(); 211 212 if (ext_opand & MT_SPM_EX_OP_DEVICES_SAVE) { 213 #ifndef MTK_PLAT_SPM_UART_UNSUPPORT 214 215 /* Notify UART to sleep */ 216 mt_uart_save(); 217 #else 218 INFO("[%s:%d] - uart not support will crash when infra off\n", 219 __func__, __LINE__); 220 #endif 221 } 222 223 if (!(ext_opand & MT_SPM_EX_OP_NON_GENERIC_RESOURCE_REQ)) { 224 /* resource request */ 225 mt_lp_rq_get_status(PLAT_RQ_REQ_USAGE, 226 &generic_spm_resource_req); 227 rc_state |= generic_spm_resource_req.val; 228 } 229 230 if (ext_opand & MT_SPM_EX_OP_DISABLE_VCORE_LP) 231 spm_lp->pwrctrl->pcm_flags1 |= SPM_FLAG1_DISABLE_VCORE_LP; 232 else 233 spm_lp->pwrctrl->pcm_flags1 &= ~SPM_FLAG1_DISABLE_VCORE_LP; 234 235 go_to_spm_before_wfi(state_id, ext_opand, spm_lp, rc_state); 236 237 spm_lock_release(); 238 239 return 0; 240 } 241 242 void spm_conservation_finish(int state_id, uint32_t ext_opand, 243 struct spm_lp_scen *spm_lp, 244 struct wake_status **status) 245 { 246 if (ext_opand & MT_SPM_EX_OP_DEVICES_SAVE) { 247 #ifndef MTK_PLAT_SPM_UART_UNSUPPORT 248 /* Notify UART to wakeup */ 249 mt_uart_restore(); 250 #else 251 INFO("[%s:%d] - uart not support will crash when infra off\n", 252 __func__, __LINE__); 253 #endif 254 } 255 256 spm_lock_get(); 257 go_to_spm_after_wfi(state_id, ext_opand, spm_lp, status); 258 spm_lock_release(); 259 } 260 261 int spm_conservation_get_result(struct wake_status **res) 262 { 263 if (!res) 264 return -1; 265 *res = &spm_wakesta; 266 return 0; 267 } 268