xref: /rk3399_ARM-atf/plat/mediatek/mt8192/plat_pm.c (revision 26f3dbe2d656e0fda9c52669af7d369c277c259e)
1 /*
2  * Copyright (c) 2020, MediaTek Inc. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /* common headers */
8 #include <assert.h>
9 
10 #include <arch_helpers.h>
11 #include <common/debug.h>
12 #include <drivers/gpio.h>
13 #include <lib/psci/psci.h>
14 
15 /* 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>
20 #include <plat_params.h>
21 #include <plat_pm.h>
22 #include <pmic.h>
23 
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 */
34 static uintptr_t secure_entrypoint;
35 /* per-CPU power state */
36 static unsigned int plat_power_state[PLATFORM_CORE_COUNT];
37 
38 /* platform CPU power domain - ops */
39 static 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 
59 static 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 
77 static 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 
107 static 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 }
121 
122 static 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 
140 static 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 
153 static 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 
173 static 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 
187 static 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 
208 static 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 
225 static 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 
243 static 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 
265 static 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 
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 static 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 
349 static 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 }
361 
362 static 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,
370 	.system_off			= plat_mtk_system_off,
371 	.validate_power_state		= plat_validate_power_state,
372 	.get_sys_suspend_power_state	= plat_get_sys_suspend_power_state
373 };
374 
375 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
376 			const plat_psci_ops_t **psci_ops)
377 {
378 	*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();
391 
392 	return 0;
393 }
394