xref: /rk3399_ARM-atf/plat/mediatek/mt8186/plat_pm.c (revision 15ca2c5e14abe415e70d08fb595973dd3e3b0af9)
1 /*
2  * Copyright (c) 2021, MediaTek Inc. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <arch_helpers.h>
9 #include <common/debug.h>
10 #include <drivers/gpio.h>
11 #include <lib/psci/psci.h>
12 
13 #include <mt_gic_v3.h>
14 #include <mtspmc.h>
15 #include <plat/common/platform.h>
16 #include <plat_mtk_lpm.h>
17 #include <plat_params.h>
18 #include <plat_pm.h>
19 #include <pmic.h>
20 #include <rtc.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 */
32 static uintptr_t secure_entrypoint;
33 /* per-CPU power state */
34 static unsigned int plat_power_state[PLATFORM_CORE_COUNT];
35 
36 /* platform CPU power domain - ops */
37 static const struct mt_lpm_tz *plat_mt_pm;
38 
39 static 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  */
55 static 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 
74 static 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  */
102 static 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 
119 static 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  */
138 static 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 
152 static 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 */
171 static 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 
185 static 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 
206 static 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 
223 static 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 
241 static 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 
264 static 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 
287 static 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 
319 static 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 
338 /*******************************************************************************
339  * MTK handlers to shutdown/reboot the system
340  ******************************************************************************/
341 static void __dead2 plat_mtk_system_reset(void)
342 {
343 	struct bl_aux_gpio_info *gpio_reset = plat_get_mtk_gpio_reset();
344 
345 	INFO("MTK System Reset\n");
346 
347 	gpio_set_value(gpio_reset->index, gpio_reset->polarity);
348 
349 	wfi();
350 	ERROR("MTK System Reset: operation not handled.\n");
351 	panic();
352 }
353 
354 static void __dead2 plat_mtk_system_off(void)
355 {
356 	INFO("MTK System Off\n");
357 
358 	rtc_power_off_sequence();
359 	pmic_power_off();
360 
361 	wfi();
362 	ERROR("MTK System Off: operation not handled.\n");
363 	panic();
364 }
365 
366 static const plat_psci_ops_t plat_psci_ops = {
367 	.cpu_standby			= plat_cpu_standby,
368 	.pwr_domain_on			= plat_power_domain_on,
369 	.pwr_domain_on_finish		= plat_power_domain_on_finish,
370 	.pwr_domain_off			= plat_power_domain_off,
371 	.pwr_domain_suspend		= plat_power_domain_suspend,
372 	.pwr_domain_suspend_finish	= plat_power_domain_suspend_finish,
373 	.validate_power_state		= plat_validate_power_state,
374 	.get_sys_suspend_power_state	= plat_get_sys_suspend_power_state,
375 	.system_off			= plat_mtk_system_off,
376 	.system_reset			= plat_mtk_system_reset,
377 };
378 
379 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
380 			const plat_psci_ops_t **psci_ops)
381 {
382 	*psci_ops = &plat_psci_ops;
383 	secure_entrypoint = sec_entrypoint;
384 
385 	/*
386 	 * init the warm reset config for boot CPU
387 	 * reset arch as AARCH64
388 	 * reset addr as function bl31_warm_entrypoint()
389 	 */
390 	mcucfg_init_archstate(0U, 0U, true);
391 	mcucfg_set_bootaddr(0U, 0U, secure_entrypoint);
392 
393 	spmc_init();
394 	plat_mt_pm = mt_plat_cpu_pm_init();
395 
396 	return 0;
397 }
398