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
spm_conservation_trace_lp(struct wake_status_trace * trace)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
spm_conservation_trace_suspend(struct wake_status_trace * trace)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
spm_conservation_wakeup_obs(int is_set,int cat,uint32_t wake_src_bits)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
go_to_spm_before_wfi(int state_id,uint32_t ext_opand,struct spm_lp_scen * spm_lp,uint32_t resource_req)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
go_to_spm_after_wfi(int state_id,uint32_t ext_opand,struct spm_lp_scen * spm_lp,struct wake_status ** status)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
spm_conservation(int state_id,uint32_t ext_opand,struct spm_lp_scen * spm_lp,uint32_t resource_req)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
spm_conservation_finish(int state_id,uint32_t ext_opand,struct spm_lp_scen * spm_lp,struct wake_status ** status)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
spm_conservation_get_result(struct wake_status ** res)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