xref: /rk3399_ARM-atf/services/spd/opteed/opteed_pm.c (revision d158d425370eb3bc1f730a412a319fdc7176d92a)
1 /*
2  * Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 
9 #include <arch_helpers.h>
10 #include <common/bl_common.h>
11 #include <common/debug.h>
12 #include <lib/el3_runtime/context_mgmt.h>
13 #include <plat/common/platform.h>
14 
15 #include "opteed_private.h"
16 
17 /*******************************************************************************
18  * The target cpu is being turned on. Allow the OPTEED/OPTEE to perform any
19  * actions needed. Nothing at the moment.
20  ******************************************************************************/
21 static void opteed_cpu_on_handler(u_register_t target_cpu)
22 {
23 }
24 
25 /*******************************************************************************
26  * This cpu is being turned off. Allow the OPTEED/OPTEE to perform any actions
27  * needed
28  ******************************************************************************/
29 static int32_t opteed_cpu_off_handler(u_register_t unused)
30 {
31 	int32_t rc = 0;
32 	uint32_t linear_id = plat_my_core_pos();
33 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
34 
35 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
36 		return 0;
37 	}
38 
39 	assert(optee_vector_table);
40 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
41 
42 	/* Program the entry point and enter OPTEE */
43 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_off_entry);
44 	rc = opteed_synchronous_sp_entry(optee_ctx);
45 
46 	/*
47 	 * Read the response from OPTEE. A non-zero return means that
48 	 * something went wrong while communicating with OPTEE.
49 	 */
50 	if (rc != 0)
51 		panic();
52 
53 	/*
54 	 * Reset OPTEE's context for a fresh start when this cpu is turned on
55 	 * subsequently.
56 	 */
57 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_OFF);
58 
59 	 return 0;
60 }
61 
62 /*******************************************************************************
63  * This cpu is being suspended. S-EL1 state must have been saved in the
64  * resident cpu (mpidr format) if it is a UP/UP migratable OPTEE.
65  ******************************************************************************/
66 static void opteed_cpu_suspend_handler(u_register_t max_off_pwrlvl)
67 {
68 	int32_t rc = 0;
69 	uint32_t linear_id = plat_my_core_pos();
70 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
71 
72 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
73 		return;
74 	}
75 
76 	assert(optee_vector_table);
77 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
78 
79 	write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx), CTX_GPREG_X0,
80 		      max_off_pwrlvl);
81 
82 	/* Program the entry point and enter OPTEE */
83 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_suspend_entry);
84 	rc = opteed_synchronous_sp_entry(optee_ctx);
85 
86 	/*
87 	 * Read the response from OPTEE. A non-zero return means that
88 	 * something went wrong while communicating with OPTEE.
89 	 */
90 	if (rc != 0)
91 		panic();
92 
93 	/* Update its context to reflect the state OPTEE is in */
94 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_SUSPEND);
95 }
96 
97 /*******************************************************************************
98  * This cpu has been turned on. Enter OPTEE to initialise S-EL1 and other bits
99  * before passing control back to the Secure Monitor. Entry in S-El1 is done
100  * after initialising minimal architectural state that guarantees safe
101  * execution.
102  ******************************************************************************/
103 void opteed_cpu_on_finish_handler(u_register_t unused)
104 {
105 	int32_t rc = 0;
106 	uint32_t linear_id = plat_my_core_pos();
107 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
108 	entry_point_info_t optee_on_entrypoint;
109 
110 	assert(optee_vector_table);
111 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_OFF ||
112 	       get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN);
113 
114 	opteed_init_optee_ep_state(&optee_on_entrypoint, opteed_rw,
115 				(uint64_t)&optee_vector_table->cpu_on_entry,
116 				0, 0, 0, 0, optee_ctx);
117 
118 	/* Initialise this cpu's secure context */
119 	cm_init_my_context(&optee_on_entrypoint);
120 
121 	/* Enter OPTEE */
122 	rc = opteed_synchronous_sp_entry(optee_ctx);
123 
124 	/*
125 	 * Read the response from OPTEE. A non-zero return means that
126 	 * something went wrong while communicating with OPTEE.
127 	 */
128 	if (rc != 0)
129 		panic();
130 
131 	/* Update its context to reflect the state OPTEE is in */
132 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
133 }
134 
135 /*******************************************************************************
136  * This cpu has resumed from suspend. The OPTEED saved the OPTEE context when it
137  * completed the preceding suspend call. Use that context to program an entry
138  * into OPTEE to allow it to do any remaining book keeping
139  ******************************************************************************/
140 static void opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl, bool abandon)
141 {
142 	int32_t rc = 0;
143 	uint32_t linear_id = plat_my_core_pos();
144 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
145 
146 	/* opteed is not expected to be used on platforms with pabandon */
147 	assert(!abandon);
148 
149 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
150 		return;
151 	}
152 
153 	assert(optee_vector_table);
154 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_SUSPEND);
155 
156 	/* Program the entry point, max_off_pwrlvl and enter the SP */
157 	write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx),
158 		      CTX_GPREG_X0,
159 		      max_off_pwrlvl);
160 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_resume_entry);
161 	rc = opteed_synchronous_sp_entry(optee_ctx);
162 
163 	/*
164 	 * Read the response from OPTEE. A non-zero return means that
165 	 * something went wrong while communicating with OPTEE.
166 	 */
167 	if (rc != 0)
168 		panic();
169 
170 	/* Update its context to reflect the state OPTEE is in */
171 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
172 }
173 
174 /*******************************************************************************
175  * Return the type of OPTEE the OPTEED is dealing with. Report the current
176  * resident cpu (mpidr format) if it is a UP/UP migratable OPTEE.
177  ******************************************************************************/
178 static int32_t opteed_cpu_migrate_info(u_register_t *resident_cpu)
179 {
180 	return OPTEE_MIGRATE_INFO;
181 }
182 
183 /*******************************************************************************
184  * System is about to be switched off. Allow the OPTEED/OPTEE to perform
185  * any actions needed.
186  ******************************************************************************/
187 static void opteed_system_off(void)
188 {
189 	uint32_t linear_id = plat_my_core_pos();
190 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
191 
192 	/*
193 	 * OP-TEE must have been initialized in order to reach this location so
194 	 * it is safe to init the CPU context if not already done for this core.
195 	 */
196 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
197 		opteed_cpu_on_finish_handler(0);
198 	}
199 
200 	assert(optee_vector_table);
201 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
202 
203 	/* Program the entry point */
204 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_off_entry);
205 
206 	/* Enter OPTEE. We do not care about the return value because we
207 	 * must continue the shutdown anyway */
208 	opteed_synchronous_sp_entry(optee_ctx);
209 }
210 
211 /*******************************************************************************
212  * System is about to be reset. Allow the OPTEED/OPTEE to perform
213  * any actions needed.
214  ******************************************************************************/
215 static void opteed_system_reset(void)
216 {
217 	uint32_t linear_id = plat_my_core_pos();
218 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
219 
220 	/*
221 	 * OP-TEE must have been initialized in order to reach this location so
222 	 * it is safe to init the CPU context if not already done for this core.
223 	 */
224 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
225 		opteed_cpu_on_finish_handler(0);
226 	}
227 
228 	assert(optee_vector_table);
229 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
230 
231 	/* Program the entry point */
232 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_reset_entry);
233 
234 	/* Enter OPTEE. We do not care about the return value because we
235 	 * must continue the reset anyway */
236 	opteed_synchronous_sp_entry(optee_ctx);
237 }
238 
239 
240 /*******************************************************************************
241  * Structure populated by the OPTEE Dispatcher to be given a chance to
242  * perform any OPTEE bookkeeping before PSCI executes a power mgmt.
243  * operation.
244  ******************************************************************************/
245 const spd_pm_ops_t opteed_pm = {
246 	.svc_on = opteed_cpu_on_handler,
247 	.svc_off = opteed_cpu_off_handler,
248 	.svc_suspend = opteed_cpu_suspend_handler,
249 	.svc_on_finish = opteed_cpu_on_finish_handler,
250 	.svc_suspend_finish = opteed_cpu_suspend_finish_handler,
251 	.svc_migrate = NULL,
252 	.svc_migrate_info = opteed_cpu_migrate_info,
253 	.svc_system_off = opteed_system_off,
254 	.svc_system_reset = opteed_system_reset,
255 };
256