xref: /rk3399_ARM-atf/plat/qemu/qemu_sbsa/sbsa_pm.c (revision 2fb5ed4737a9652d5a74ac20331221dcd4523252)
1*2fb5ed47SGraeme Gregory /*
2*2fb5ed47SGraeme Gregory  * Copyright (c) 2020, Nuvia Inc
3*2fb5ed47SGraeme Gregory  * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved.
4*2fb5ed47SGraeme Gregory  *
5*2fb5ed47SGraeme Gregory  * SPDX-License-Identifier: BSD-3-Clause
6*2fb5ed47SGraeme Gregory  */
7*2fb5ed47SGraeme Gregory 
8*2fb5ed47SGraeme Gregory 
9*2fb5ed47SGraeme Gregory #include <arch_helpers.h>
10*2fb5ed47SGraeme Gregory #include <assert.h>
11*2fb5ed47SGraeme Gregory #include <lib/mmio.h>
12*2fb5ed47SGraeme Gregory #include <lib/psci/psci.h>
13*2fb5ed47SGraeme Gregory #include <plat/common/platform.h>
14*2fb5ed47SGraeme Gregory 
15*2fb5ed47SGraeme Gregory #include <platform_def.h>
16*2fb5ed47SGraeme Gregory #include "sbsa_private.h"
17*2fb5ed47SGraeme Gregory 
18*2fb5ed47SGraeme Gregory #define ADP_STOPPED_APPLICATION_EXIT 0x20026
19*2fb5ed47SGraeme Gregory 
20*2fb5ed47SGraeme Gregory /*
21*2fb5ed47SGraeme Gregory  * Define offset and commands for the fake EC device
22*2fb5ed47SGraeme Gregory  */
23*2fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_OFFSET 0x50000000
24*2fb5ed47SGraeme Gregory 
25*2fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_CMD_SHUTDOWN 0x01
26*2fb5ed47SGraeme Gregory #define SBSA_SECURE_EC_CMD_REBOOT   0x02
27*2fb5ed47SGraeme Gregory 
28*2fb5ed47SGraeme Gregory /*
29*2fb5ed47SGraeme Gregory  * The secure entry point to be used on warm reset.
30*2fb5ed47SGraeme Gregory  */
31*2fb5ed47SGraeme Gregory static unsigned long secure_entrypoint;
32*2fb5ed47SGraeme Gregory 
33*2fb5ed47SGraeme Gregory /* Make composite power state parameter till power level 0 */
34*2fb5ed47SGraeme Gregory #if PSCI_EXTENDED_STATE_ID
35*2fb5ed47SGraeme Gregory 
36*2fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
37*2fb5ed47SGraeme Gregory 		(((lvl0_state) << PSTATE_ID_SHIFT) | \
38*2fb5ed47SGraeme Gregory 		 ((type) << PSTATE_TYPE_SHIFT))
39*2fb5ed47SGraeme Gregory #else
40*2fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
41*2fb5ed47SGraeme Gregory 		(((lvl0_state) << PSTATE_ID_SHIFT) | \
42*2fb5ed47SGraeme Gregory 		 ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \
43*2fb5ed47SGraeme Gregory 		 ((type) << PSTATE_TYPE_SHIFT))
44*2fb5ed47SGraeme Gregory #endif /* PSCI_EXTENDED_STATE_ID */
45*2fb5ed47SGraeme Gregory 
46*2fb5ed47SGraeme Gregory 
47*2fb5ed47SGraeme Gregory #define qemu_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \
48*2fb5ed47SGraeme Gregory 		(((lvl1_state) << PLAT_LOCAL_PSTATE_WIDTH) | \
49*2fb5ed47SGraeme Gregory 		 qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type))
50*2fb5ed47SGraeme Gregory 
51*2fb5ed47SGraeme Gregory 
52*2fb5ed47SGraeme Gregory 
53*2fb5ed47SGraeme Gregory /*
54*2fb5ed47SGraeme Gregory  *  The table storing the valid idle power states. Ensure that the
55*2fb5ed47SGraeme Gregory  *  array entries are populated in ascending order of state-id to
56*2fb5ed47SGraeme Gregory  *  enable us to use binary search during power state validation.
57*2fb5ed47SGraeme Gregory  *  The table must be terminated by a NULL entry.
58*2fb5ed47SGraeme Gregory  */
59*2fb5ed47SGraeme Gregory static const unsigned int qemu_pm_idle_states[] = {
60*2fb5ed47SGraeme Gregory 	/* State-id - 0x01 */
61*2fb5ed47SGraeme Gregory 	qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_RET,
62*2fb5ed47SGraeme Gregory 				MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY),
63*2fb5ed47SGraeme Gregory 	/* State-id - 0x02 */
64*2fb5ed47SGraeme Gregory 	qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_OFF,
65*2fb5ed47SGraeme Gregory 				MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN),
66*2fb5ed47SGraeme Gregory 	/* State-id - 0x22 */
67*2fb5ed47SGraeme Gregory 	qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_OFF, PLAT_LOCAL_STATE_OFF,
68*2fb5ed47SGraeme Gregory 				MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN),
69*2fb5ed47SGraeme Gregory 	0
70*2fb5ed47SGraeme Gregory };
71*2fb5ed47SGraeme Gregory 
72*2fb5ed47SGraeme Gregory /*******************************************************************************
73*2fb5ed47SGraeme Gregory  * Platform handler called to check the validity of the power state
74*2fb5ed47SGraeme Gregory  * parameter. The power state parameter has to be a composite power state.
75*2fb5ed47SGraeme Gregory  ******************************************************************************/
76*2fb5ed47SGraeme Gregory static int qemu_validate_power_state(unsigned int power_state,
77*2fb5ed47SGraeme Gregory 				psci_power_state_t *req_state)
78*2fb5ed47SGraeme Gregory {
79*2fb5ed47SGraeme Gregory 	unsigned int state_id;
80*2fb5ed47SGraeme Gregory 	unsigned int i;
81*2fb5ed47SGraeme Gregory 
82*2fb5ed47SGraeme Gregory 	assert(req_state != NULL);
83*2fb5ed47SGraeme Gregory 
84*2fb5ed47SGraeme Gregory 	/*
85*2fb5ed47SGraeme Gregory 	 *  Currently we are using a linear search for finding the matching
86*2fb5ed47SGraeme Gregory 	 *  entry in the idle power state array. This can be made a binary
87*2fb5ed47SGraeme Gregory 	 *  search if the number of entries justifies the additional complexity.
88*2fb5ed47SGraeme Gregory 	 */
89*2fb5ed47SGraeme Gregory 	for (i = 0U; qemu_pm_idle_states[i] != 0U; i++) {
90*2fb5ed47SGraeme Gregory 		if (power_state == qemu_pm_idle_states[i]) {
91*2fb5ed47SGraeme Gregory 			break;
92*2fb5ed47SGraeme Gregory 		}
93*2fb5ed47SGraeme Gregory 	}
94*2fb5ed47SGraeme Gregory 
95*2fb5ed47SGraeme Gregory 	/* Return error if entry not found in the idle state array */
96*2fb5ed47SGraeme Gregory 	if (qemu_pm_idle_states[i] == 0U) {
97*2fb5ed47SGraeme Gregory 		return PSCI_E_INVALID_PARAMS;
98*2fb5ed47SGraeme Gregory 	}
99*2fb5ed47SGraeme Gregory 
100*2fb5ed47SGraeme Gregory 	i = 0U;
101*2fb5ed47SGraeme Gregory 	state_id = psci_get_pstate_id(power_state);
102*2fb5ed47SGraeme Gregory 
103*2fb5ed47SGraeme Gregory 	/* Parse the State ID and populate the state info parameter */
104*2fb5ed47SGraeme Gregory 	while (state_id != 0U) {
105*2fb5ed47SGraeme Gregory 		req_state->pwr_domain_state[i++] = state_id &
106*2fb5ed47SGraeme Gregory 						PLAT_LOCAL_PSTATE_MASK;
107*2fb5ed47SGraeme Gregory 		state_id >>= PLAT_LOCAL_PSTATE_WIDTH;
108*2fb5ed47SGraeme Gregory 	}
109*2fb5ed47SGraeme Gregory 
110*2fb5ed47SGraeme Gregory 	return PSCI_E_SUCCESS;
111*2fb5ed47SGraeme Gregory }
112*2fb5ed47SGraeme Gregory 
113*2fb5ed47SGraeme Gregory /*******************************************************************************
114*2fb5ed47SGraeme Gregory  * Platform handler called when a CPU is about to enter standby.
115*2fb5ed47SGraeme Gregory  ******************************************************************************/
116*2fb5ed47SGraeme Gregory static void qemu_cpu_standby(plat_local_state_t cpu_state)
117*2fb5ed47SGraeme Gregory {
118*2fb5ed47SGraeme Gregory 
119*2fb5ed47SGraeme Gregory 	assert(cpu_state == PLAT_LOCAL_STATE_RET);
120*2fb5ed47SGraeme Gregory 
121*2fb5ed47SGraeme Gregory 	/*
122*2fb5ed47SGraeme Gregory 	 * Enter standby state
123*2fb5ed47SGraeme Gregory 	 * dsb is good practice before using wfi to enter low power states
124*2fb5ed47SGraeme Gregory 	 */
125*2fb5ed47SGraeme Gregory 	dsb();
126*2fb5ed47SGraeme Gregory 	wfi();
127*2fb5ed47SGraeme Gregory }
128*2fb5ed47SGraeme Gregory 
129*2fb5ed47SGraeme Gregory /*******************************************************************************
130*2fb5ed47SGraeme Gregory  * Platform handler called when a power domain is about to be turned on. The
131*2fb5ed47SGraeme Gregory  * mpidr determines the CPU to be turned on.
132*2fb5ed47SGraeme Gregory  ******************************************************************************/
133*2fb5ed47SGraeme Gregory static int qemu_pwr_domain_on(u_register_t mpidr)
134*2fb5ed47SGraeme Gregory {
135*2fb5ed47SGraeme Gregory 	int pos = plat_core_pos_by_mpidr(mpidr);
136*2fb5ed47SGraeme Gregory 	uint64_t *hold_base = (uint64_t *)PLAT_QEMU_HOLD_BASE;
137*2fb5ed47SGraeme Gregory 
138*2fb5ed47SGraeme Gregory 	if (pos < 0) {
139*2fb5ed47SGraeme Gregory 		return PSCI_E_INVALID_PARAMS;
140*2fb5ed47SGraeme Gregory 	}
141*2fb5ed47SGraeme Gregory 
142*2fb5ed47SGraeme Gregory 	hold_base[pos] = PLAT_QEMU_HOLD_STATE_GO;
143*2fb5ed47SGraeme Gregory 	dsb();
144*2fb5ed47SGraeme Gregory 	sev();
145*2fb5ed47SGraeme Gregory 
146*2fb5ed47SGraeme Gregory 	return PSCI_E_SUCCESS;
147*2fb5ed47SGraeme Gregory }
148*2fb5ed47SGraeme Gregory 
149*2fb5ed47SGraeme Gregory /*******************************************************************************
150*2fb5ed47SGraeme Gregory  * Platform handler called when a power domain is about to be turned off. The
151*2fb5ed47SGraeme Gregory  * target_state encodes the power state that each level should transition to.
152*2fb5ed47SGraeme Gregory  ******************************************************************************/
153*2fb5ed47SGraeme Gregory static void qemu_pwr_domain_off(const psci_power_state_t *target_state)
154*2fb5ed47SGraeme Gregory {
155*2fb5ed47SGraeme Gregory 	qemu_pwr_gic_off();
156*2fb5ed47SGraeme Gregory }
157*2fb5ed47SGraeme Gregory 
158*2fb5ed47SGraeme Gregory void __dead2 plat_secondary_cold_boot_setup(void);
159*2fb5ed47SGraeme Gregory 
160*2fb5ed47SGraeme Gregory static void __dead2
161*2fb5ed47SGraeme Gregory qemu_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
162*2fb5ed47SGraeme Gregory {
163*2fb5ed47SGraeme Gregory 	disable_mmu_el3();
164*2fb5ed47SGraeme Gregory 	plat_secondary_cold_boot_setup();
165*2fb5ed47SGraeme Gregory }
166*2fb5ed47SGraeme Gregory 
167*2fb5ed47SGraeme Gregory /*******************************************************************************
168*2fb5ed47SGraeme Gregory  * Platform handler called when a power domain is about to be suspended. The
169*2fb5ed47SGraeme Gregory  * target_state encodes the power state that each level should transition to.
170*2fb5ed47SGraeme Gregory  ******************************************************************************/
171*2fb5ed47SGraeme Gregory void qemu_pwr_domain_suspend(const psci_power_state_t *target_state)
172*2fb5ed47SGraeme Gregory {
173*2fb5ed47SGraeme Gregory 	assert(false);
174*2fb5ed47SGraeme Gregory }
175*2fb5ed47SGraeme Gregory 
176*2fb5ed47SGraeme Gregory /*******************************************************************************
177*2fb5ed47SGraeme Gregory  * Platform handler called when a power domain has just been powered on after
178*2fb5ed47SGraeme Gregory  * being turned off earlier. The target_state encodes the low power state that
179*2fb5ed47SGraeme Gregory  * each level has woken up from.
180*2fb5ed47SGraeme Gregory  ******************************************************************************/
181*2fb5ed47SGraeme Gregory void qemu_pwr_domain_on_finish(const psci_power_state_t *target_state)
182*2fb5ed47SGraeme Gregory {
183*2fb5ed47SGraeme Gregory 	assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] ==
184*2fb5ed47SGraeme Gregory 					PLAT_LOCAL_STATE_OFF);
185*2fb5ed47SGraeme Gregory 
186*2fb5ed47SGraeme Gregory 	qemu_pwr_gic_on_finish();
187*2fb5ed47SGraeme Gregory }
188*2fb5ed47SGraeme Gregory 
189*2fb5ed47SGraeme Gregory /*******************************************************************************
190*2fb5ed47SGraeme Gregory  * Platform handler called when a power domain has just been powered on after
191*2fb5ed47SGraeme Gregory  * having been suspended earlier. The target_state encodes the low power state
192*2fb5ed47SGraeme Gregory  * that each level has woken up from.
193*2fb5ed47SGraeme Gregory  ******************************************************************************/
194*2fb5ed47SGraeme Gregory void qemu_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
195*2fb5ed47SGraeme Gregory {
196*2fb5ed47SGraeme Gregory 	assert(false);
197*2fb5ed47SGraeme Gregory }
198*2fb5ed47SGraeme Gregory 
199*2fb5ed47SGraeme Gregory /*******************************************************************************
200*2fb5ed47SGraeme Gregory  * Platform handlers to shutdown/reboot the system
201*2fb5ed47SGraeme Gregory  ******************************************************************************/
202*2fb5ed47SGraeme Gregory static void __dead2 qemu_system_off(void)
203*2fb5ed47SGraeme Gregory {
204*2fb5ed47SGraeme Gregory 	mmio_write_32(SBSA_SECURE_EC_OFFSET, SBSA_SECURE_EC_CMD_SHUTDOWN);
205*2fb5ed47SGraeme Gregory 	panic();
206*2fb5ed47SGraeme Gregory }
207*2fb5ed47SGraeme Gregory 
208*2fb5ed47SGraeme Gregory static void __dead2 qemu_system_reset(void)
209*2fb5ed47SGraeme Gregory {
210*2fb5ed47SGraeme Gregory 	mmio_write_32(SBSA_SECURE_EC_OFFSET, SBSA_SECURE_EC_CMD_REBOOT);
211*2fb5ed47SGraeme Gregory 	panic();
212*2fb5ed47SGraeme Gregory }
213*2fb5ed47SGraeme Gregory 
214*2fb5ed47SGraeme Gregory static const plat_psci_ops_t plat_qemu_psci_pm_ops = {
215*2fb5ed47SGraeme Gregory 	.cpu_standby = qemu_cpu_standby,
216*2fb5ed47SGraeme Gregory 	.pwr_domain_on = qemu_pwr_domain_on,
217*2fb5ed47SGraeme Gregory 	.pwr_domain_off = qemu_pwr_domain_off,
218*2fb5ed47SGraeme Gregory 	.pwr_domain_pwr_down_wfi = qemu_pwr_domain_pwr_down_wfi,
219*2fb5ed47SGraeme Gregory 	.pwr_domain_suspend = qemu_pwr_domain_suspend,
220*2fb5ed47SGraeme Gregory 	.pwr_domain_on_finish = qemu_pwr_domain_on_finish,
221*2fb5ed47SGraeme Gregory 	.pwr_domain_suspend_finish = qemu_pwr_domain_suspend_finish,
222*2fb5ed47SGraeme Gregory 	.system_off = qemu_system_off,
223*2fb5ed47SGraeme Gregory 	.system_reset = qemu_system_reset,
224*2fb5ed47SGraeme Gregory 	.validate_power_state = qemu_validate_power_state
225*2fb5ed47SGraeme Gregory };
226*2fb5ed47SGraeme Gregory 
227*2fb5ed47SGraeme Gregory int plat_setup_psci_ops(uintptr_t sec_entrypoint,
228*2fb5ed47SGraeme Gregory 			const plat_psci_ops_t **psci_ops)
229*2fb5ed47SGraeme Gregory {
230*2fb5ed47SGraeme Gregory 	uintptr_t *mailbox = (uintptr_t *)PLAT_QEMU_TRUSTED_MAILBOX_BASE;
231*2fb5ed47SGraeme Gregory 
232*2fb5ed47SGraeme Gregory 	*mailbox = sec_entrypoint;
233*2fb5ed47SGraeme Gregory 	secure_entrypoint = (unsigned long)sec_entrypoint;
234*2fb5ed47SGraeme Gregory 	*psci_ops = &plat_qemu_psci_pm_ops;
235*2fb5ed47SGraeme Gregory 
236*2fb5ed47SGraeme Gregory 	return 0;
237*2fb5ed47SGraeme Gregory }
238