blob: ac6cb8085e5bdc1c88244f089a6b6608f6a679da [file] [log] [blame]
developer2189d3a2020-04-17 17:14:23 +08001/*
2 * Copyright (c) 2020, MediaTek Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7/* common headers */
developer0b0c04d2020-06-16 11:48:36 +08008#include <assert.h>
9
developerbd31bf02020-07-02 12:10:13 +080010#include <arch_helpers.h>
11#include <common/debug.h>
12#include <drivers/gpio.h>
developer2189d3a2020-04-17 17:14:23 +080013#include <lib/psci/psci.h>
14
developer0b0c04d2020-06-16 11:48:36 +080015/* platform specific headers */
16#include <mt_gic_v3.h>
17#include <mtspmc.h>
18#include <plat/common/platform.h>
19#include <plat_mtk_lpm.h>
developerbd31bf02020-07-02 12:10:13 +080020#include <plat_params.h>
developer0b0c04d2020-06-16 11:48:36 +080021#include <plat_pm.h>
developerea0c4732020-08-12 16:32:10 +080022#include <pmic.h>
developer0b0c04d2020-06-16 11:48:36 +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
41#define plat_mt_pm_invoke(_name, _cpu, _state) ({ \
42 int ret = -1; \
43 if (plat_mt_pm != NULL && plat_mt_pm->_name != NULL) { \
44 ret = plat_mt_pm->_name(_cpu, _state); \
45 } \
46 ret; })
47
48#define plat_mt_pm_invoke_no_check(_name, _cpu, _state) ({ \
49 if (plat_mt_pm != NULL && plat_mt_pm->_name != NULL) { \
50 (void) plat_mt_pm->_name(_cpu, _state); \
51 } \
52 })
53
54/*
55 * Common MTK_platform operations to power on/off a
56 * CPU in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
57 */
58
59static void plat_cpu_pwrdwn_common(unsigned int cpu,
60 const psci_power_state_t *state, unsigned int req_pstate)
61{
62 assert(cpu == plat_my_core_pos());
63
64 plat_mt_pm_invoke_no_check(pwr_cpu_dwn, cpu, state);
65
66 if ((psci_get_pstate_pwrlvl(req_pstate) >= MTK_AFFLVL_CLUSTER) ||
67 (req_pstate == 0U)) { /* hotplug off */
68 coordinate_cluster_pwroff();
69 }
70
71 /* Prevent interrupts from spuriously waking up this CPU */
72 mt_gic_rdistif_save();
73 gicv3_cpuif_disable(cpu);
74 gicv3_rdistif_off(cpu);
75}
76
77static void plat_cpu_pwron_common(unsigned int cpu,
78 const psci_power_state_t *state, unsigned int req_pstate)
79{
80 assert(cpu == plat_my_core_pos());
81
82 plat_mt_pm_invoke_no_check(pwr_cpu_on, cpu, state);
83
84 coordinate_cluster_pwron();
85
86 /* Enable the GIC CPU interface */
87 gicv3_rdistif_on(cpu);
88 gicv3_cpuif_enable(cpu);
89 mt_gic_rdistif_init();
90
91 /*
92 * If mcusys does power down before then restore
93 * all CPUs' GIC Redistributors
94 */
95 if (IS_MCUSYS_OFF_STATE(state)) {
96 mt_gic_rdistif_restore_all();
97 } else {
98 mt_gic_rdistif_restore();
99 }
100}
101
102/*
103 * Common MTK_platform operations to power on/off a
104 * cluster in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
105 */
106
107static void plat_cluster_pwrdwn_common(unsigned int cpu,
108 const psci_power_state_t *state, unsigned int req_pstate)
109{
110 assert(cpu == plat_my_core_pos());
111
112 if (plat_mt_pm_invoke(pwr_cluster_dwn, cpu, state) != 0) {
113 coordinate_cluster_pwron();
114
115 /* TODO: return on fail.
116 * Add a 'return' here before adding any code following
117 * the if-block.
118 */
119 }
120}
developer2189d3a2020-04-17 17:14:23 +0800121
developer0b0c04d2020-06-16 11:48:36 +0800122static void plat_cluster_pwron_common(unsigned int cpu,
123 const psci_power_state_t *state, unsigned int req_pstate)
124{
125 assert(cpu == plat_my_core_pos());
126
127 if (plat_mt_pm_invoke(pwr_cluster_on, cpu, state) != 0) {
128 /* TODO: return on fail.
129 * Add a 'return' here before adding any code following
130 * the if-block.
131 */
132 }
133}
134
135/*
136 * Common MTK_platform operations to power on/off a
137 * mcusys in response to a CPU_ON, CPU_OFF or CPU_SUSPEND request.
138 */
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
145 if (plat_mt_pm_invoke(pwr_mcusys_dwn, cpu, state) != 0) {
146 return; /* return on fail */
147 }
148
149 mt_gic_distif_save();
150 gic_sgi_save_all();
151}
152
153static void plat_mcusys_pwron_common(unsigned int cpu,
154 const psci_power_state_t *state, unsigned int req_pstate)
155{
156 assert(cpu == plat_my_core_pos());
157
158 if (plat_mt_pm_invoke(pwr_mcusys_on, cpu, state) != 0) {
159 return; /* return on fail */
160 }
161
162 mt_gic_init();
163 mt_gic_distif_restore();
164 gic_sgi_restore_all();
165
166 plat_mt_pm_invoke_no_check(pwr_mcusys_on_finished, cpu, state);
167}
168
169/*
170 * plat_psci_ops implementation
171 */
172
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
249 plat_mt_pm_invoke_no_check(pwr_prompt, cpu, state);
250
251 /* Perform the common CPU specific operations */
252 plat_cpu_pwrdwn_common(cpu, state, plat_power_state[cpu]);
253
254 if (IS_CLUSTER_OFF_STATE(state)) {
255 /* Perform the common cluster specific operations */
256 plat_cluster_pwrdwn_common(cpu, state, plat_power_state[cpu]);
257 }
258
259 if (IS_MCUSYS_OFF_STATE(state)) {
260 /* Perform the common mcusys specific operations */
261 plat_mcusys_pwrdwn_common(cpu, state, plat_power_state[cpu]);
262 }
263}
264
265static void plat_power_domain_suspend_finish(const psci_power_state_t *state)
266{
267 unsigned int cpu = plat_my_core_pos();
268
269 assert(cpu < PLATFORM_CORE_COUNT);
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 plat_mt_pm_invoke_no_check(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
developerea0c4732020-08-12 16:32:10 +0800338static void __dead2 plat_mtk_system_off(void)
339{
340 INFO("MTK System Off\n");
341
342 pmic_power_off();
343
344 wfi();
345 ERROR("MTK System Off: operation not handled.\n");
346 panic();
347}
348
developerbd31bf02020-07-02 12:10:13 +0800349static void __dead2 plat_mtk_system_reset(void)
350{
351 struct bl_aux_gpio_info *gpio_reset = plat_get_mtk_gpio_reset();
352
353 INFO("MTK System Reset\n");
354
355 gpio_set_value(gpio_reset->index, gpio_reset->polarity);
356
357 wfi();
358 ERROR("MTK System Reset: operation not handled.\n");
359 panic();
360}
developer2189d3a2020-04-17 17:14:23 +0800361
developer0b0c04d2020-06-16 11:48:36 +0800362static const plat_psci_ops_t plat_psci_ops = {
363 .system_reset = plat_mtk_system_reset,
364 .cpu_standby = plat_cpu_standby,
365 .pwr_domain_on = plat_power_domain_on,
366 .pwr_domain_on_finish = plat_power_domain_on_finish,
367 .pwr_domain_off = plat_power_domain_off,
368 .pwr_domain_suspend = plat_power_domain_suspend,
369 .pwr_domain_suspend_finish = plat_power_domain_suspend_finish,
developerea0c4732020-08-12 16:32:10 +0800370 .system_off = plat_mtk_system_off,
developer0b0c04d2020-06-16 11:48:36 +0800371 .validate_power_state = plat_validate_power_state,
372 .get_sys_suspend_power_state = plat_get_sys_suspend_power_state
developer2189d3a2020-04-17 17:14:23 +0800373};
374
375int plat_setup_psci_ops(uintptr_t sec_entrypoint,
376 const plat_psci_ops_t **psci_ops)
377{
developer0b0c04d2020-06-16 11:48:36 +0800378 *psci_ops = &plat_psci_ops;
379 secure_entrypoint = sec_entrypoint;
380
381 /*
382 * init the warm reset config for boot CPU
383 * reset arch as AARCH64
384 * reset addr as function bl31_warm_entrypoint()
385 */
386 mcucfg_init_archstate(0U, 0U, true);
387 mcucfg_set_bootaddr(0U, 0U, secure_entrypoint);
388
389 spmc_init();
390 plat_mt_pm = mt_plat_cpu_pm_init();
developer2189d3a2020-04-17 17:14:23 +0800391
392 return 0;
393}