xref: /rk3399_ARM-atf/plat/xilinx/zynqmp/plat_psci.c (revision 61ef376aa2fa85b6eec23d314e7e308f80d18b8d)
1 /*
2  * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <arch_helpers.h>
8 #include <assert.h>
9 #include <debug.h>
10 #include <errno.h>
11 #include <gicv2.h>
12 #include <mmio.h>
13 #include <plat_arm.h>
14 #include <platform.h>
15 #include <psci.h>
16 #include "pm_api_sys.h"
17 #include "pm_client.h"
18 #include "zynqmp_private.h"
19 
20 uintptr_t zynqmp_sec_entry;
21 
22 void zynqmp_cpu_standby(plat_local_state_t cpu_state)
23 {
24 	VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state);
25 
26 	dsb();
27 	wfi();
28 }
29 
30 static int zynqmp_nopmu_pwr_domain_on(u_register_t mpidr)
31 {
32 	uint32_t r;
33 	unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr);
34 
35 	VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
36 
37 	if (cpu_id == -1)
38 		return PSCI_E_INTERN_FAIL;
39 
40 	/* program RVBAR */
41 	mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry);
42 	mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32);
43 
44 	/* clear VINITHI */
45 	r = mmio_read_32(APU_CONFIG_0);
46 	r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id);
47 	mmio_write_32(APU_CONFIG_0, r);
48 
49 	/* clear power down request */
50 	r = mmio_read_32(APU_PWRCTL);
51 	r &= ~(1 << cpu_id);
52 	mmio_write_32(APU_PWRCTL, r);
53 
54 	/* power up island */
55 	mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id);
56 	mmio_write_32(PMU_GLOBAL_REQ_PWRUP_TRIG, 1 << cpu_id);
57 	/* FIXME: we should have a way to break out */
58 	while (mmio_read_32(PMU_GLOBAL_REQ_PWRUP_STATUS) & (1 << cpu_id))
59 		;
60 
61 	/* release core reset */
62 	r = mmio_read_32(CRF_APB_RST_FPD_APU);
63 	r &= ~((CRF_APB_RST_FPD_APU_ACPU_PWRON_RESET |
64 			CRF_APB_RST_FPD_APU_ACPU_RESET) << cpu_id);
65 	mmio_write_32(CRF_APB_RST_FPD_APU, r);
66 
67 	return PSCI_E_SUCCESS;
68 }
69 
70 static int zynqmp_pwr_domain_on(u_register_t mpidr)
71 {
72 	unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr);
73 	const struct pm_proc *proc;
74 
75 	VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
76 
77 	if (cpu_id == -1)
78 		return PSCI_E_INTERN_FAIL;
79 
80 	proc = pm_get_proc(cpu_id);
81 	/* Clear power down request */
82 	pm_client_wakeup(proc);
83 
84 	/* Send request to PMU to wake up selected APU CPU core */
85 	pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING);
86 
87 	return PSCI_E_SUCCESS;
88 }
89 
90 static void zynqmp_nopmu_pwr_domain_off(const psci_power_state_t *target_state)
91 {
92 	uint32_t r;
93 	unsigned int cpu_id = plat_my_core_pos();
94 
95 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
96 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
97 			__func__, i, target_state->pwr_domain_state[i]);
98 
99 	/* Prevent interrupts from spuriously waking up this cpu */
100 	gicv2_cpuif_disable();
101 
102 	/* set power down request */
103 	r = mmio_read_32(APU_PWRCTL);
104 	r |= (1 << cpu_id);
105 	mmio_write_32(APU_PWRCTL, r);
106 }
107 
108 static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state)
109 {
110 	unsigned int cpu_id = plat_my_core_pos();
111 	const struct pm_proc *proc = pm_get_proc(cpu_id);
112 
113 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
114 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
115 			__func__, i, target_state->pwr_domain_state[i]);
116 
117 	/* Prevent interrupts from spuriously waking up this cpu */
118 	gicv2_cpuif_disable();
119 
120 	/*
121 	 * Send request to PMU to power down the appropriate APU CPU
122 	 * core.
123 	 * According to PSCI specification, CPU_off function does not
124 	 * have resume address and CPU core can only be woken up
125 	 * invoking CPU_on function, during which resume address will
126 	 * be set.
127 	 */
128 	pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
129 }
130 
131 static void zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t *target_state)
132 {
133 	uint32_t r;
134 	unsigned int cpu_id = plat_my_core_pos();
135 
136 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
137 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
138 			__func__, i, target_state->pwr_domain_state[i]);
139 
140 	/* set power down request */
141 	r = mmio_read_32(APU_PWRCTL);
142 	r |= (1 << cpu_id);
143 	mmio_write_32(APU_PWRCTL, r);
144 
145 	/* program RVBAR */
146 	mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry);
147 	mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32);
148 
149 	/* clear VINITHI */
150 	r = mmio_read_32(APU_CONFIG_0);
151 	r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id);
152 	mmio_write_32(APU_CONFIG_0, r);
153 
154 	/* enable power up on IRQ */
155 	mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id);
156 }
157 
158 static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
159 {
160 	unsigned int state;
161 	unsigned int cpu_id = plat_my_core_pos();
162 	const struct pm_proc *proc = pm_get_proc(cpu_id);
163 
164 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
165 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
166 			__func__, i, target_state->pwr_domain_state[i]);
167 
168 	state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
169 		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
170 
171 	/* Send request to PMU to suspend this core */
172 	pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry);
173 
174 	/* APU is to be turned off */
175 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
176 		/* disable coherency */
177 		plat_arm_interconnect_exit_coherency();
178 	}
179 }
180 
181 static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state)
182 {
183 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
184 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
185 			__func__, i, target_state->pwr_domain_state[i]);
186 
187 	gicv2_cpuif_enable();
188 	gicv2_pcpu_distif_init();
189 }
190 
191 static void zynqmp_nopmu_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
192 {
193 	uint32_t r;
194 	unsigned int cpu_id = plat_my_core_pos();
195 
196 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
197 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
198 			__func__, i, target_state->pwr_domain_state[i]);
199 
200 	/* disable power up on IRQ */
201 	mmio_write_32(PMU_GLOBAL_REQ_PWRUP_DIS, 1 << cpu_id);
202 
203 	/* clear powerdown bit */
204 	r = mmio_read_32(APU_PWRCTL);
205 	r &= ~(1 << cpu_id);
206 	mmio_write_32(APU_PWRCTL, r);
207 }
208 
209 static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
210 {
211 	unsigned int cpu_id = plat_my_core_pos();
212 	const struct pm_proc *proc = pm_get_proc(cpu_id);
213 
214 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
215 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
216 			__func__, i, target_state->pwr_domain_state[i]);
217 
218 	/* Clear the APU power control register for this cpu */
219 	pm_client_wakeup(proc);
220 
221 	/* enable coherency */
222 	plat_arm_interconnect_enter_coherency();
223 	/* APU was turned off */
224 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
225 		plat_arm_gic_init();
226 	} else {
227 		gicv2_cpuif_enable();
228 		gicv2_pcpu_distif_init();
229 	}
230 }
231 
232 /*******************************************************************************
233  * ZynqMP handlers to shutdown/reboot the system
234  ******************************************************************************/
235 static void __dead2 zynqmp_nopmu_system_off(void)
236 {
237 	ERROR("ZynqMP System Off: operation not handled.\n");
238 
239 	/* disable coherency */
240 	plat_arm_interconnect_exit_coherency();
241 
242 	panic();
243 }
244 
245 static void __dead2 zynqmp_system_off(void)
246 {
247 	/* disable coherency */
248 	plat_arm_interconnect_exit_coherency();
249 
250 	/* Send the power down request to the PMU */
251 	pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
252 			   pm_get_shutdown_scope());
253 
254 	while (1)
255 		wfi();
256 }
257 
258 static void __dead2 zynqmp_nopmu_system_reset(void)
259 {
260 	/*
261 	 * This currently triggers a system reset. I.e. the whole
262 	 * system will be reset! Including RPUs, PMU, PL, etc.
263 	 */
264 
265 	/* disable coherency */
266 	plat_arm_interconnect_exit_coherency();
267 
268 	/* bypass RPLL (needed on 1.0 silicon) */
269 	uint32_t reg = mmio_read_32(CRL_APB_RPLL_CTRL);
270 	reg |= CRL_APB_RPLL_CTRL_BYPASS;
271 	mmio_write_32(CRL_APB_RPLL_CTRL, reg);
272 
273 	/* trigger system reset */
274 	mmio_write_32(CRL_APB_RESET_CTRL, CRL_APB_RESET_CTRL_SOFT_RESET);
275 
276 	while (1)
277 		wfi();
278 }
279 
280 static void __dead2 zynqmp_system_reset(void)
281 {
282 	/* disable coherency */
283 	plat_arm_interconnect_exit_coherency();
284 
285 	/* Send the system reset request to the PMU */
286 	pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET,
287 			   pm_get_shutdown_scope());
288 
289 	while (1)
290 		wfi();
291 }
292 
293 int zynqmp_validate_power_state(unsigned int power_state,
294 				psci_power_state_t *req_state)
295 {
296 	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
297 
298 	int pstate = psci_get_pstate_type(power_state);
299 
300 	assert(req_state);
301 
302 	/* Sanity check the requested state */
303 	if (pstate == PSTATE_TYPE_STANDBY)
304 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
305 	else
306 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
307 
308 	/* We expect the 'state id' to be zero */
309 	if (psci_get_pstate_id(power_state))
310 		return PSCI_E_INVALID_PARAMS;
311 
312 	return PSCI_E_SUCCESS;
313 }
314 
315 int zynqmp_validate_ns_entrypoint(unsigned long ns_entrypoint)
316 {
317 	VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint);
318 
319 	/* FIXME: Actually validate */
320 	return PSCI_E_SUCCESS;
321 }
322 
323 void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state)
324 {
325 	req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
326 	req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
327 }
328 
329 /*******************************************************************************
330  * Export the platform handlers to enable psci to invoke them
331  ******************************************************************************/
332 static const struct plat_psci_ops zynqmp_psci_ops = {
333 	.cpu_standby			= zynqmp_cpu_standby,
334 	.pwr_domain_on			= zynqmp_pwr_domain_on,
335 	.pwr_domain_off			= zynqmp_pwr_domain_off,
336 	.pwr_domain_suspend		= zynqmp_pwr_domain_suspend,
337 	.pwr_domain_on_finish		= zynqmp_pwr_domain_on_finish,
338 	.pwr_domain_suspend_finish	= zynqmp_pwr_domain_suspend_finish,
339 	.system_off			= zynqmp_system_off,
340 	.system_reset			= zynqmp_system_reset,
341 	.validate_power_state		= zynqmp_validate_power_state,
342 	.validate_ns_entrypoint		= zynqmp_validate_ns_entrypoint,
343 	.get_sys_suspend_power_state	= zynqmp_get_sys_suspend_power_state,
344 };
345 
346 static const struct plat_psci_ops zynqmp_nopmu_psci_ops = {
347 	.cpu_standby			= zynqmp_cpu_standby,
348 	.pwr_domain_on			= zynqmp_nopmu_pwr_domain_on,
349 	.pwr_domain_off			= zynqmp_nopmu_pwr_domain_off,
350 	.pwr_domain_suspend		= zynqmp_nopmu_pwr_domain_suspend,
351 	.pwr_domain_on_finish		= zynqmp_pwr_domain_on_finish,
352 	.pwr_domain_suspend_finish	= zynqmp_nopmu_pwr_domain_suspend_finish,
353 	.system_off			= zynqmp_nopmu_system_off,
354 	.system_reset			= zynqmp_nopmu_system_reset,
355 	.validate_power_state		= zynqmp_validate_power_state,
356 	.validate_ns_entrypoint		= zynqmp_validate_ns_entrypoint,
357 	.get_sys_suspend_power_state	= zynqmp_get_sys_suspend_power_state,
358 };
359 
360 /*******************************************************************************
361  * Export the platform specific power ops.
362  ******************************************************************************/
363 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
364 			const struct plat_psci_ops **psci_ops)
365 {
366 	zynqmp_sec_entry = sec_entrypoint;
367 
368 	if (zynqmp_is_pmu_up())
369 		*psci_ops = &zynqmp_psci_ops;
370 	else
371 		*psci_ops = &zynqmp_nopmu_psci_ops;
372 
373 	return 0;
374 }
375