xref: /arm-trusted-firmware/plat/xilinx/zynqmp/plat_psci.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved.
4*91f16700Schasinglulu  *
5*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
6*91f16700Schasinglulu  */
7*91f16700Schasinglulu 
8*91f16700Schasinglulu #include <assert.h>
9*91f16700Schasinglulu #include <errno.h>
10*91f16700Schasinglulu 
11*91f16700Schasinglulu #include <arch_helpers.h>
12*91f16700Schasinglulu #include <common/debug.h>
13*91f16700Schasinglulu #include <drivers/arm/gicv2.h>
14*91f16700Schasinglulu #include <lib/mmio.h>
15*91f16700Schasinglulu #include <lib/psci/psci.h>
16*91f16700Schasinglulu #include <plat/arm/common/plat_arm.h>
17*91f16700Schasinglulu #include <plat/common/platform.h>
18*91f16700Schasinglulu 
19*91f16700Schasinglulu #include <plat_private.h>
20*91f16700Schasinglulu #include "pm_client.h"
21*91f16700Schasinglulu #include "zynqmp_pm_api_sys.h"
22*91f16700Schasinglulu 
23*91f16700Schasinglulu static uintptr_t zynqmp_sec_entry;
24*91f16700Schasinglulu 
25*91f16700Schasinglulu static void zynqmp_cpu_standby(plat_local_state_t cpu_state)
26*91f16700Schasinglulu {
27*91f16700Schasinglulu 	VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state);
28*91f16700Schasinglulu 
29*91f16700Schasinglulu 	dsb();
30*91f16700Schasinglulu 	wfi();
31*91f16700Schasinglulu }
32*91f16700Schasinglulu 
33*91f16700Schasinglulu static int32_t zynqmp_pwr_domain_on(u_register_t mpidr)
34*91f16700Schasinglulu {
35*91f16700Schasinglulu 	uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
36*91f16700Schasinglulu 	const struct pm_proc *proc;
37*91f16700Schasinglulu 	uint32_t buff[3];
38*91f16700Schasinglulu 	enum pm_ret_status ret;
39*91f16700Schasinglulu 
40*91f16700Schasinglulu 	VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
41*91f16700Schasinglulu 
42*91f16700Schasinglulu 	if (cpu_id == -1) {
43*91f16700Schasinglulu 		return PSCI_E_INTERN_FAIL;
44*91f16700Schasinglulu 	}
45*91f16700Schasinglulu 	proc = pm_get_proc(cpu_id);
46*91f16700Schasinglulu 
47*91f16700Schasinglulu 	/* Check the APU proc status before wakeup */
48*91f16700Schasinglulu 	ret = pm_get_node_status(proc->node_id, buff);
49*91f16700Schasinglulu 	if ((ret != PM_RET_SUCCESS) || (buff[0] == PM_PROC_STATE_SUSPENDING)) {
50*91f16700Schasinglulu 		return PSCI_E_INTERN_FAIL;
51*91f16700Schasinglulu 	}
52*91f16700Schasinglulu 
53*91f16700Schasinglulu 	/* Clear power down request */
54*91f16700Schasinglulu 	pm_client_wakeup(proc);
55*91f16700Schasinglulu 
56*91f16700Schasinglulu 	/* Send request to PMU to wake up selected APU CPU core */
57*91f16700Schasinglulu 	pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING);
58*91f16700Schasinglulu 
59*91f16700Schasinglulu 	return PSCI_E_SUCCESS;
60*91f16700Schasinglulu }
61*91f16700Schasinglulu 
62*91f16700Schasinglulu static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state)
63*91f16700Schasinglulu {
64*91f16700Schasinglulu 	uint32_t cpu_id = plat_my_core_pos();
65*91f16700Schasinglulu 	const struct pm_proc *proc = pm_get_proc(cpu_id);
66*91f16700Schasinglulu 
67*91f16700Schasinglulu 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
68*91f16700Schasinglulu 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
69*91f16700Schasinglulu 			__func__, i, target_state->pwr_domain_state[i]);
70*91f16700Schasinglulu 	}
71*91f16700Schasinglulu 
72*91f16700Schasinglulu 	/* Prevent interrupts from spuriously waking up this cpu */
73*91f16700Schasinglulu 	gicv2_cpuif_disable();
74*91f16700Schasinglulu 
75*91f16700Schasinglulu 	/*
76*91f16700Schasinglulu 	 * Send request to PMU to power down the appropriate APU CPU
77*91f16700Schasinglulu 	 * core.
78*91f16700Schasinglulu 	 * According to PSCI specification, CPU_off function does not
79*91f16700Schasinglulu 	 * have resume address and CPU core can only be woken up
80*91f16700Schasinglulu 	 * invoking CPU_on function, during which resume address will
81*91f16700Schasinglulu 	 * be set.
82*91f16700Schasinglulu 	 */
83*91f16700Schasinglulu 	pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
84*91f16700Schasinglulu }
85*91f16700Schasinglulu 
86*91f16700Schasinglulu static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
87*91f16700Schasinglulu {
88*91f16700Schasinglulu 	uint32_t state;
89*91f16700Schasinglulu 	uint32_t cpu_id = plat_my_core_pos();
90*91f16700Schasinglulu 	const struct pm_proc *proc = pm_get_proc(cpu_id);
91*91f16700Schasinglulu 
92*91f16700Schasinglulu 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
93*91f16700Schasinglulu 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
94*91f16700Schasinglulu 			__func__, i, target_state->pwr_domain_state[i]);
95*91f16700Schasinglulu 
96*91f16700Schasinglulu 	state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
97*91f16700Schasinglulu 		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
98*91f16700Schasinglulu 
99*91f16700Schasinglulu 	/* Send request to PMU to suspend this core */
100*91f16700Schasinglulu 	pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry);
101*91f16700Schasinglulu 
102*91f16700Schasinglulu 	/* APU is to be turned off */
103*91f16700Schasinglulu 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
104*91f16700Schasinglulu 		/* disable coherency */
105*91f16700Schasinglulu 		plat_arm_interconnect_exit_coherency();
106*91f16700Schasinglulu 	}
107*91f16700Schasinglulu }
108*91f16700Schasinglulu 
109*91f16700Schasinglulu static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state)
110*91f16700Schasinglulu {
111*91f16700Schasinglulu 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
112*91f16700Schasinglulu 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
113*91f16700Schasinglulu 			__func__, i, target_state->pwr_domain_state[i]);
114*91f16700Schasinglulu 	}
115*91f16700Schasinglulu 	plat_arm_gic_pcpu_init();
116*91f16700Schasinglulu 	gicv2_cpuif_enable();
117*91f16700Schasinglulu }
118*91f16700Schasinglulu 
119*91f16700Schasinglulu static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
120*91f16700Schasinglulu {
121*91f16700Schasinglulu 	uint32_t cpu_id = plat_my_core_pos();
122*91f16700Schasinglulu 	const struct pm_proc *proc = pm_get_proc(cpu_id);
123*91f16700Schasinglulu 
124*91f16700Schasinglulu 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
125*91f16700Schasinglulu 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
126*91f16700Schasinglulu 			__func__, i, target_state->pwr_domain_state[i]);
127*91f16700Schasinglulu 	}
128*91f16700Schasinglulu 
129*91f16700Schasinglulu 	/* Clear the APU power control register for this cpu */
130*91f16700Schasinglulu 	pm_client_wakeup(proc);
131*91f16700Schasinglulu 
132*91f16700Schasinglulu 	/* enable coherency */
133*91f16700Schasinglulu 	plat_arm_interconnect_enter_coherency();
134*91f16700Schasinglulu 	/* APU was turned off */
135*91f16700Schasinglulu 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
136*91f16700Schasinglulu 		plat_arm_gic_init();
137*91f16700Schasinglulu 	} else {
138*91f16700Schasinglulu 		gicv2_cpuif_enable();
139*91f16700Schasinglulu 		gicv2_pcpu_distif_init();
140*91f16700Schasinglulu 	}
141*91f16700Schasinglulu }
142*91f16700Schasinglulu 
143*91f16700Schasinglulu /*******************************************************************************
144*91f16700Schasinglulu  * ZynqMP handlers to shutdown/reboot the system
145*91f16700Schasinglulu  ******************************************************************************/
146*91f16700Schasinglulu 
147*91f16700Schasinglulu static void __dead2 zynqmp_system_off(void)
148*91f16700Schasinglulu {
149*91f16700Schasinglulu 	/* disable coherency */
150*91f16700Schasinglulu 	plat_arm_interconnect_exit_coherency();
151*91f16700Schasinglulu 
152*91f16700Schasinglulu 	/* Send the power down request to the PMU */
153*91f16700Schasinglulu 	pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
154*91f16700Schasinglulu 			   pm_get_shutdown_scope());
155*91f16700Schasinglulu 
156*91f16700Schasinglulu 	while (1) {
157*91f16700Schasinglulu 		wfi();
158*91f16700Schasinglulu 	}
159*91f16700Schasinglulu }
160*91f16700Schasinglulu 
161*91f16700Schasinglulu static void __dead2 zynqmp_system_reset(void)
162*91f16700Schasinglulu {
163*91f16700Schasinglulu 	/* disable coherency */
164*91f16700Schasinglulu 	plat_arm_interconnect_exit_coherency();
165*91f16700Schasinglulu 
166*91f16700Schasinglulu 	/* Send the system reset request to the PMU */
167*91f16700Schasinglulu 	pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET,
168*91f16700Schasinglulu 			   pm_get_shutdown_scope());
169*91f16700Schasinglulu 
170*91f16700Schasinglulu 	while (1) {
171*91f16700Schasinglulu 		wfi();
172*91f16700Schasinglulu 	}
173*91f16700Schasinglulu }
174*91f16700Schasinglulu 
175*91f16700Schasinglulu static int32_t zynqmp_validate_power_state(uint32_t power_state,
176*91f16700Schasinglulu 				psci_power_state_t *req_state)
177*91f16700Schasinglulu {
178*91f16700Schasinglulu 	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
179*91f16700Schasinglulu 
180*91f16700Schasinglulu 	uint32_t pstate = psci_get_pstate_type(power_state);
181*91f16700Schasinglulu 
182*91f16700Schasinglulu 	assert(req_state);
183*91f16700Schasinglulu 
184*91f16700Schasinglulu 	/* Sanity check the requested state */
185*91f16700Schasinglulu 	if (pstate == PSTATE_TYPE_STANDBY) {
186*91f16700Schasinglulu 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
187*91f16700Schasinglulu 	} else {
188*91f16700Schasinglulu 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
189*91f16700Schasinglulu 	}
190*91f16700Schasinglulu 	/* We expect the 'state id' to be zero */
191*91f16700Schasinglulu 	if (psci_get_pstate_id(power_state)) {
192*91f16700Schasinglulu 		return PSCI_E_INVALID_PARAMS;
193*91f16700Schasinglulu 	}
194*91f16700Schasinglulu 
195*91f16700Schasinglulu 	return PSCI_E_SUCCESS;
196*91f16700Schasinglulu }
197*91f16700Schasinglulu 
198*91f16700Schasinglulu static void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state)
199*91f16700Schasinglulu {
200*91f16700Schasinglulu 	req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
201*91f16700Schasinglulu 	req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
202*91f16700Schasinglulu }
203*91f16700Schasinglulu 
204*91f16700Schasinglulu /*******************************************************************************
205*91f16700Schasinglulu  * Export the platform handlers to enable psci to invoke them
206*91f16700Schasinglulu  ******************************************************************************/
207*91f16700Schasinglulu static const struct plat_psci_ops zynqmp_psci_ops = {
208*91f16700Schasinglulu 	.cpu_standby			= zynqmp_cpu_standby,
209*91f16700Schasinglulu 	.pwr_domain_on			= zynqmp_pwr_domain_on,
210*91f16700Schasinglulu 	.pwr_domain_off			= zynqmp_pwr_domain_off,
211*91f16700Schasinglulu 	.pwr_domain_suspend		= zynqmp_pwr_domain_suspend,
212*91f16700Schasinglulu 	.pwr_domain_on_finish		= zynqmp_pwr_domain_on_finish,
213*91f16700Schasinglulu 	.pwr_domain_suspend_finish	= zynqmp_pwr_domain_suspend_finish,
214*91f16700Schasinglulu 	.system_off			= zynqmp_system_off,
215*91f16700Schasinglulu 	.system_reset			= zynqmp_system_reset,
216*91f16700Schasinglulu 	.validate_power_state		= zynqmp_validate_power_state,
217*91f16700Schasinglulu 	.get_sys_suspend_power_state	= zynqmp_get_sys_suspend_power_state,
218*91f16700Schasinglulu };
219*91f16700Schasinglulu 
220*91f16700Schasinglulu /*******************************************************************************
221*91f16700Schasinglulu  * Export the platform specific power ops.
222*91f16700Schasinglulu  ******************************************************************************/
223*91f16700Schasinglulu int plat_setup_psci_ops(uintptr_t sec_entrypoint,
224*91f16700Schasinglulu 			const struct plat_psci_ops **psci_ops)
225*91f16700Schasinglulu {
226*91f16700Schasinglulu 	zynqmp_sec_entry = sec_entrypoint;
227*91f16700Schasinglulu 
228*91f16700Schasinglulu 	*psci_ops = &zynqmp_psci_ops;
229*91f16700Schasinglulu 
230*91f16700Schasinglulu 	return 0;
231*91f16700Schasinglulu }
232