xref: /rk3399_ARM-atf/plat/mediatek/drivers/spm/mt8189/mt_spm_conservation.c (revision 3ba36ea07ca22c748b5adcf5d9bff00e752681d7)
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