blob: 607470f89bac060ef7ea8319b482c8a73a299640 [file] [log] [blame]
Rex-BC Chen749b2112021-09-28 11:24:09 +08001/*
2 * Copyright (c) 2021, MediaTek Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
developerc3dabd82021-11-08 11:30:40 +08007/* common headers */
8#include <assert.h>
9
10#include <arch_helpers.h>
James Lo4ac7a412021-10-06 18:12:30 +080011#include <common/debug.h>
Rex-BC Chen749b2112021-09-28 11:24:09 +080012#include <lib/psci/psci.h>
13
developerc3dabd82021-11-08 11:30:40 +080014/* platform specific headers */
15#include <mt_gic_v3.h>
16#include <mtspmc.h>
17#include <plat/common/platform.h>
18#include <plat_mtk_lpm.h>
19#include <plat_params.h>
20#include <plat_pm.h>
21
22/*
23 * Cluster state request:
24 * [0] : The CPU requires cluster power down
25 * [1] : The CPU requires cluster power on
26 */
27#define coordinate_cluster(onoff) write_clusterpwrdn_el1(onoff)
28#define coordinate_cluster_pwron() coordinate_cluster(1)
29#define coordinate_cluster_pwroff() coordinate_cluster(0)
30
31/* platform secure entry point */
32static uintptr_t secure_entrypoint;
33/* per-CPU power state */
34static unsigned int plat_power_state[PLATFORM_CORE_COUNT];
35
36/* platform CPU power domain - ops */
37static const struct mt_lpm_tz *plat_mt_pm;
38
39static inline int plat_mt_pm_invoke(int (*func)(unsigned int cpu,
40 const psci_power_state_t *state),
41 int cpu, const psci_power_state_t *state)
42{
43 int ret = -1;
44
45 if (func != NULL) {
46 ret = func(cpu, state);
47 }
48 return ret;
49}
50
51/*
52 * Common MTK_platform operations to power on/off a
53 * CPU in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
54 */
55static void plat_cpu_pwrdwn_common(unsigned int cpu,
56 const psci_power_state_t *state, unsigned int req_pstate)
57{
58 assert(cpu == plat_my_core_pos());
59 assert(plat_mt_pm != NULL);
60
61 (void)plat_mt_pm_invoke(plat_mt_pm->pwr_cpu_dwn, cpu, state);
62
63 if ((psci_get_pstate_pwrlvl(req_pstate) >= MTK_AFFLVL_CLUSTER) ||
64 (req_pstate == 0U)) { /* hotplug off */
65 coordinate_cluster_pwroff();
66 }
67
68 /* Prevent interrupts from spuriously waking up this CPU */
69 mt_gic_rdistif_save();
70 gicv3_cpuif_disable(cpu);
71 gicv3_rdistif_off(cpu);
72}
73
74static void plat_cpu_pwron_common(unsigned int cpu,
75 const psci_power_state_t *state, unsigned int req_pstate)
76{
77 assert(cpu == plat_my_core_pos());
78 assert(plat_mt_pm != NULL);
79
80 (void)plat_mt_pm_invoke(plat_mt_pm->pwr_cpu_on, cpu, state);
81
82 coordinate_cluster_pwron();
83
84 /*
85 * If mcusys does power down before then restore
86 * all CPUs' GIC Redistributors
87 */
88 if (IS_MCUSYS_OFF_STATE(state)) {
89 mt_gic_rdistif_restore_all();
90 } else {
91 gicv3_rdistif_on(cpu);
92 gicv3_cpuif_enable(cpu);
93 mt_gic_rdistif_init();
94 mt_gic_rdistif_restore();
95 }
96}
97
98/*
99 * Common MTK_platform operations to power on/off a
100 * cluster in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
101 */
102static void plat_cluster_pwrdwn_common(unsigned int cpu,
103 const psci_power_state_t *state, unsigned int req_pstate)
104{
105 assert(cpu == plat_my_core_pos());
106 assert(plat_mt_pm != NULL);
107
108 if (plat_mt_pm_invoke(plat_mt_pm->pwr_cluster_dwn, cpu, state) != 0) {
109 coordinate_cluster_pwron();
110
111 /*
112 * TODO:
113 * Return on fail and add a 'return' here before
114 * adding any code following the if-block.
115 */
116 }
117}
118
119static void plat_cluster_pwron_common(unsigned int cpu,
120 const psci_power_state_t *state, unsigned int req_pstate)
121{
122 assert(cpu == plat_my_core_pos());
123 assert(plat_mt_pm != NULL);
124
125 if (plat_mt_pm_invoke(plat_mt_pm->pwr_cluster_on, cpu, state) != 0) {
126 /*
127 * TODO:
128 * return on fail and add a 'return' here before
129 * adding any code following the if-block.
130 */
131 }
132}
133
134/*
135 * Common MTK_platform operations to power on/off a
136 * mcusys in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
137 */
138static void plat_mcusys_pwrdwn_common(unsigned int cpu,
139 const psci_power_state_t *state, unsigned int req_pstate)
140{
141 assert(cpu == plat_my_core_pos());
142 assert(plat_mt_pm != NULL);
143
144 if (plat_mt_pm_invoke(plat_mt_pm->pwr_mcusys_dwn, cpu, state) != 0) {
145 return; /* return on fail */
146 }
147
148 mt_gic_distif_save();
149 gic_sgi_save_all();
150}
151
152static void plat_mcusys_pwron_common(unsigned int cpu,
153 const psci_power_state_t *state, unsigned int req_pstate)
154{
155 assert(cpu == plat_my_core_pos());
156 assert(plat_mt_pm != NULL);
157
158 if (plat_mt_pm_invoke(plat_mt_pm->pwr_mcusys_on, cpu, state) != 0) {
159 /* return on fail */
160 return;
161 }
162
163 mt_gic_init();
164 mt_gic_distif_restore();
165 gic_sgi_restore_all();
166
167 (void)plat_mt_pm_invoke(plat_mt_pm->pwr_mcusys_on_finished, cpu, state);
168}
169
170/* plat_psci_ops implementation */
171static void plat_cpu_standby(plat_local_state_t cpu_state)
172{
173 uint64_t scr;
174
175 scr = read_scr_el3();
176 write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
177
178 isb();
179 dsb();
180 wfi();
181
182 write_scr_el3(scr);
183}
184
185static int plat_power_domain_on(u_register_t mpidr)
186{
187 unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr);
188 unsigned int cluster = 0U;
189
190 if (cpu >= PLATFORM_CORE_COUNT) {
191 return PSCI_E_INVALID_PARAMS;
192 }
193
194 if (!spm_get_cluster_powerstate(cluster)) {
195 spm_poweron_cluster(cluster);
196 }
197
198 /* init CPU reset arch as AARCH64 */
199 mcucfg_init_archstate(cluster, cpu, true);
200 mcucfg_set_bootaddr(cluster, cpu, secure_entrypoint);
201 spm_poweron_cpu(cluster, cpu);
202
203 return PSCI_E_SUCCESS;
204}
205
206static void plat_power_domain_on_finish(const psci_power_state_t *state)
207{
208 unsigned long mpidr = read_mpidr_el1();
209 unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr);
210
211 assert(cpu < PLATFORM_CORE_COUNT);
212
213 /* Allow IRQs to wakeup this core in IDLE flow */
214 mcucfg_enable_gic_wakeup(0U, cpu);
215
216 if (IS_CLUSTER_OFF_STATE(state)) {
217 plat_cluster_pwron_common(cpu, state, 0U);
218 }
219
220 plat_cpu_pwron_common(cpu, state, 0U);
221}
222
223static void plat_power_domain_off(const psci_power_state_t *state)
224{
225 unsigned long mpidr = read_mpidr_el1();
226 unsigned int cpu = (unsigned int)plat_core_pos_by_mpidr(mpidr);
227
228 assert(cpu < PLATFORM_CORE_COUNT);
229
230 plat_cpu_pwrdwn_common(cpu, state, 0U);
231 spm_poweroff_cpu(0U, cpu);
232
233 /* prevent unintended IRQs from waking up the hot-unplugged core */
234 mcucfg_disable_gic_wakeup(0U, cpu);
235
236 if (IS_CLUSTER_OFF_STATE(state)) {
237 plat_cluster_pwrdwn_common(cpu, state, 0U);
238 }
239}
240
241static void plat_power_domain_suspend(const psci_power_state_t *state)
242{
243 unsigned int cpu = plat_my_core_pos();
244
245 assert(cpu < PLATFORM_CORE_COUNT);
246 assert(plat_mt_pm != NULL);
247
248 (void)plat_mt_pm_invoke(plat_mt_pm->pwr_prompt, cpu, state);
249
250 /* Perform the common CPU specific operations */
251 plat_cpu_pwrdwn_common(cpu, state, plat_power_state[cpu]);
252
253 if (IS_CLUSTER_OFF_STATE(state)) {
254 /* Perform the common cluster specific operations */
255 plat_cluster_pwrdwn_common(cpu, state, plat_power_state[cpu]);
256 }
257
258 if (IS_MCUSYS_OFF_STATE(state)) {
259 /* Perform the common mcusys specific operations */
260 plat_mcusys_pwrdwn_common(cpu, state, plat_power_state[cpu]);
261 }
262}
263
264static void plat_power_domain_suspend_finish(const psci_power_state_t *state)
265{
266 unsigned int cpu = plat_my_core_pos();
267
268 assert(cpu < PLATFORM_CORE_COUNT);
269 assert(plat_mt_pm != NULL);
270
271 if (IS_MCUSYS_OFF_STATE(state)) {
272 /* Perform the common mcusys specific operations */
273 plat_mcusys_pwron_common(cpu, state, plat_power_state[cpu]);
274 }
275
276 if (IS_CLUSTER_OFF_STATE(state)) {
277 /* Perform the common cluster specific operations */
278 plat_cluster_pwron_common(cpu, state, plat_power_state[cpu]);
279 }
280
281 /* Perform the common CPU specific operations */
282 plat_cpu_pwron_common(cpu, state, plat_power_state[cpu]);
283
284 (void)plat_mt_pm_invoke(plat_mt_pm->pwr_reflect, cpu, state);
285}
286
287static int plat_validate_power_state(unsigned int power_state,
288 psci_power_state_t *req_state)
289{
290 unsigned int pstate = psci_get_pstate_type(power_state);
291 unsigned int aff_lvl = psci_get_pstate_pwrlvl(power_state);
292 unsigned int cpu = plat_my_core_pos();
293
294 if (aff_lvl > PLAT_MAX_PWR_LVL) {
295 return PSCI_E_INVALID_PARAMS;
296 }
297
298 if (pstate == PSTATE_TYPE_STANDBY) {
299 req_state->pwr_domain_state[0] = PLAT_MAX_RET_STATE;
300 } else {
301 unsigned int i;
302 unsigned int pstate_id = psci_get_pstate_id(power_state);
303 plat_local_state_t s = MTK_LOCAL_STATE_OFF;
304
305 /* Use pstate_id to be power domain state */
306 if (pstate_id > s) {
307 s = (plat_local_state_t)pstate_id;
308 }
309
310 for (i = 0U; i <= aff_lvl; i++) {
311 req_state->pwr_domain_state[i] = s;
312 }
313 }
314
315 plat_power_state[cpu] = power_state;
316 return PSCI_E_SUCCESS;
317}
318
319static void plat_get_sys_suspend_power_state(psci_power_state_t *req_state)
320{
321 unsigned int lv;
322 unsigned int cpu = plat_my_core_pos();
323
324 for (lv = PSCI_CPU_PWR_LVL; lv <= PLAT_MAX_PWR_LVL; lv++) {
325 req_state->pwr_domain_state[lv] = PLAT_MAX_OFF_STATE;
326 }
327
328 plat_power_state[cpu] =
329 psci_make_powerstate(
330 MT_PLAT_PWR_STATE_SYSTEM_SUSPEND,
331 PSTATE_TYPE_POWERDOWN, PLAT_MAX_PWR_LVL);
332
333 flush_dcache_range((uintptr_t)
334 &plat_power_state[cpu],
335 sizeof(plat_power_state[cpu]));
336}
337
Rex-BC Chen749b2112021-09-28 11:24:09 +0800338static const plat_psci_ops_t plat_psci_ops = {
developerc3dabd82021-11-08 11:30:40 +0800339 .cpu_standby = plat_cpu_standby,
340 .pwr_domain_on = plat_power_domain_on,
341 .pwr_domain_on_finish = plat_power_domain_on_finish,
342 .pwr_domain_off = plat_power_domain_off,
343 .pwr_domain_suspend = plat_power_domain_suspend,
344 .pwr_domain_suspend_finish = plat_power_domain_suspend_finish,
345 .validate_power_state = plat_validate_power_state,
346 .get_sys_suspend_power_state = plat_get_sys_suspend_power_state
Rex-BC Chen749b2112021-09-28 11:24:09 +0800347};
348
349int plat_setup_psci_ops(uintptr_t sec_entrypoint,
350 const plat_psci_ops_t **psci_ops)
351{
352 *psci_ops = &plat_psci_ops;
developerc3dabd82021-11-08 11:30:40 +0800353 secure_entrypoint = sec_entrypoint;
354
355 /*
356 * init the warm reset config for boot CPU
357 * reset arch as AARCH64
358 * reset addr as function bl31_warm_entrypoint()
359 */
360 mcucfg_init_archstate(0U, 0U, true);
361 mcucfg_set_bootaddr(0U, 0U, secure_entrypoint);
362
363 spmc_init();
developerba7b7d22021-11-14 10:14:45 +0800364 plat_mt_pm = mt_plat_cpu_pm_init();
Rex-BC Chen749b2112021-09-28 11:24:09 +0800365
366 return 0;
367}