blob: 116211c51ed18fbc15a2755e2413f9bd40730c7d [file] [log] [blame]
Jens Wiklander52c798e2015-12-07 14:37:10 +01001/*
Hongbo Zhang32338ec2018-04-19 13:06:07 +08002 * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved.
Jens Wiklander52c798e2015-12-07 14:37:10 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Jens Wiklander52c798e2015-12-07 14:37:10 +01005 */
6
Jens Wiklander52c798e2015-12-07 14:37:10 +01007#include <assert.h>
Isla Mitchelle3631462017-07-14 10:46:32 +01008#include <platform_def.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00009
10#include <arch_helpers.h>
11#include <common/debug.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000012#include <lib/psci/psci.h>
Andrew Walbranf3a68392020-01-15 14:18:04 +000013#include <lib/semihosting.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000014#include <plat/common/platform.h>
Jens Wiklander52c798e2015-12-07 14:37:10 +010015
Hongbo Zhang32338ec2018-04-19 13:06:07 +080016#include "qemu_private.h"
17
Andrew Walbranf3a68392020-01-15 14:18:04 +000018#define ADP_STOPPED_APPLICATION_EXIT 0x20026
19
Jens Wiklander52c798e2015-12-07 14:37:10 +010020/*
21 * The secure entry point to be used on warm reset.
22 */
23static unsigned long secure_entrypoint;
24
25/* Make composite power state parameter till power level 0 */
26#if PSCI_EXTENDED_STATE_ID
27
28#define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
29 (((lvl0_state) << PSTATE_ID_SHIFT) | \
30 ((type) << PSTATE_TYPE_SHIFT))
31#else
32#define qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \
33 (((lvl0_state) << PSTATE_ID_SHIFT) | \
34 ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \
35 ((type) << PSTATE_TYPE_SHIFT))
36#endif /* PSCI_EXTENDED_STATE_ID */
37
38
39#define qemu_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \
40 (((lvl1_state) << PLAT_LOCAL_PSTATE_WIDTH) | \
41 qemu_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type))
42
43
44
45/*
46 * The table storing the valid idle power states. Ensure that the
47 * array entries are populated in ascending order of state-id to
48 * enable us to use binary search during power state validation.
49 * The table must be terminated by a NULL entry.
50 */
51static const unsigned int qemu_pm_idle_states[] = {
52 /* State-id - 0x01 */
53 qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_RET,
54 MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY),
55 /* State-id - 0x02 */
56 qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_RUN, PLAT_LOCAL_STATE_OFF,
57 MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN),
58 /* State-id - 0x22 */
59 qemu_make_pwrstate_lvl1(PLAT_LOCAL_STATE_OFF, PLAT_LOCAL_STATE_OFF,
60 MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN),
61 0,
62};
63
64/*******************************************************************************
65 * Platform handler called to check the validity of the power state
66 * parameter. The power state parameter has to be a composite power state.
67 ******************************************************************************/
68static int qemu_validate_power_state(unsigned int power_state,
69 psci_power_state_t *req_state)
70{
71 unsigned int state_id;
72 int i;
73
74 assert(req_state);
75
76 /*
77 * Currently we are using a linear search for finding the matching
78 * entry in the idle power state array. This can be made a binary
79 * search if the number of entries justify the additional complexity.
80 */
81 for (i = 0; !!qemu_pm_idle_states[i]; i++) {
82 if (power_state == qemu_pm_idle_states[i])
83 break;
84 }
85
86 /* Return error if entry not found in the idle state array */
87 if (!qemu_pm_idle_states[i])
88 return PSCI_E_INVALID_PARAMS;
89
90 i = 0;
91 state_id = psci_get_pstate_id(power_state);
92
93 /* Parse the State ID and populate the state info parameter */
94 while (state_id) {
95 req_state->pwr_domain_state[i++] = state_id &
96 PLAT_LOCAL_PSTATE_MASK;
97 state_id >>= PLAT_LOCAL_PSTATE_WIDTH;
98 }
99
100 return PSCI_E_SUCCESS;
101}
102
103/*******************************************************************************
104 * Platform handler called to check the validity of the non secure
105 * entrypoint.
106 ******************************************************************************/
107static int qemu_validate_ns_entrypoint(uintptr_t entrypoint)
108{
109 /*
110 * Check if the non secure entrypoint lies within the non
111 * secure DRAM.
112 */
113 if ((entrypoint >= NS_DRAM0_BASE) &&
114 (entrypoint < (NS_DRAM0_BASE + NS_DRAM0_SIZE)))
115 return PSCI_E_SUCCESS;
116 return PSCI_E_INVALID_ADDRESS;
117}
118
119/*******************************************************************************
120 * Platform handler called when a CPU is about to enter standby.
121 ******************************************************************************/
122static void qemu_cpu_standby(plat_local_state_t cpu_state)
123{
124
125 assert(cpu_state == PLAT_LOCAL_STATE_RET);
126
127 /*
128 * Enter standby state
129 * dsb is good practice before using wfi to enter low power states
130 */
131 dsb();
132 wfi();
133}
134
135/*******************************************************************************
136 * Platform handler called when a power domain is about to be turned on. The
137 * mpidr determines the CPU to be turned on.
138 ******************************************************************************/
139static int qemu_pwr_domain_on(u_register_t mpidr)
140{
141 int rc = PSCI_E_SUCCESS;
142 unsigned pos = plat_core_pos_by_mpidr(mpidr);
143 uint64_t *hold_base = (uint64_t *)PLAT_QEMU_HOLD_BASE;
144
145 hold_base[pos] = PLAT_QEMU_HOLD_STATE_GO;
146 sev();
147
148 return rc;
149}
150
151/*******************************************************************************
152 * Platform handler called when a power domain is about to be turned off. The
153 * target_state encodes the power state that each level should transition to.
154 ******************************************************************************/
155void qemu_pwr_domain_off(const psci_power_state_t *target_state)
156{
157 assert(0);
158}
159
160/*******************************************************************************
161 * Platform handler called when a power domain is about to be suspended. The
162 * target_state encodes the power state that each level should transition to.
163 ******************************************************************************/
164void qemu_pwr_domain_suspend(const psci_power_state_t *target_state)
165{
166 assert(0);
167}
168
169/*******************************************************************************
170 * Platform handler called when a power domain has just been powered on after
171 * being turned off earlier. The target_state encodes the low power state that
172 * each level has woken up from.
173 ******************************************************************************/
174void qemu_pwr_domain_on_finish(const psci_power_state_t *target_state)
175{
176 assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] ==
177 PLAT_LOCAL_STATE_OFF);
178
Hongbo Zhang32338ec2018-04-19 13:06:07 +0800179 qemu_pwr_gic_on_finish();
Jens Wiklander52c798e2015-12-07 14:37:10 +0100180}
181
182/*******************************************************************************
183 * Platform handler called when a power domain has just been powered on after
184 * having been suspended earlier. The target_state encodes the low power state
185 * that each level has woken up from.
186 ******************************************************************************/
187void qemu_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
188{
189 assert(0);
190}
191
192/*******************************************************************************
193 * Platform handlers to shutdown/reboot the system
194 ******************************************************************************/
195static void __dead2 qemu_system_off(void)
196{
Andrew Walbranf3a68392020-01-15 14:18:04 +0000197 semihosting_exit(ADP_STOPPED_APPLICATION_EXIT, 0);
198 ERROR("QEMU System Off: semihosting call unexpectedly returned.\n");
Jens Wiklander52c798e2015-12-07 14:37:10 +0100199 panic();
200}
201
202static void __dead2 qemu_system_reset(void)
203{
204 ERROR("QEMU System Reset: operation not handled.\n");
205 panic();
206}
207
208static const plat_psci_ops_t plat_qemu_psci_pm_ops = {
209 .cpu_standby = qemu_cpu_standby,
210 .pwr_domain_on = qemu_pwr_domain_on,
211 .pwr_domain_off = qemu_pwr_domain_off,
212 .pwr_domain_suspend = qemu_pwr_domain_suspend,
213 .pwr_domain_on_finish = qemu_pwr_domain_on_finish,
214 .pwr_domain_suspend_finish = qemu_pwr_domain_suspend_finish,
215 .system_off = qemu_system_off,
216 .system_reset = qemu_system_reset,
217 .validate_power_state = qemu_validate_power_state,
218 .validate_ns_entrypoint = qemu_validate_ns_entrypoint
219};
220
221int plat_setup_psci_ops(uintptr_t sec_entrypoint,
222 const plat_psci_ops_t **psci_ops)
223{
224 uintptr_t *mailbox = (void *) PLAT_QEMU_TRUSTED_MAILBOX_BASE;
225
226 *mailbox = sec_entrypoint;
227 secure_entrypoint = (unsigned long) sec_entrypoint;
228 *psci_ops = &plat_qemu_psci_pm_ops;
229
230 return 0;
231}