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