blob: d28100d9c1354e07ed8dc5ad62a645d21459d8a7 [file] [log] [blame]
Tony Xief6118cc2016-01-15 17:17:32 +08001/*
2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <arch_helpers.h>
32#include <assert.h>
33#include <console.h>
34#include <errno.h>
35#include <debug.h>
36#include <psci.h>
37#include <delay_timer.h>
38#include <platform_def.h>
39#include <plat_private.h>
40
41/* Macros to read the rk power domain state */
42#define RK_CORE_PWR_STATE(state) \
43 ((state)->pwr_domain_state[MPIDR_AFFLVL0])
44#define RK_CLUSTER_PWR_STATE(state) \
45 ((state)->pwr_domain_state[MPIDR_AFFLVL1])
46#define RK_SYSTEM_PWR_STATE(state) \
47 ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
48
49static uintptr_t rockchip_sec_entrypoint;
50
51static struct rockchip_pm_ops_cb *rockchip_ops;
52
Tony Xief6118cc2016-01-15 17:17:32 +080053/*******************************************************************************
54 * Rockchip standard platform handler called to check the validity of the power
55 * state parameter.
56 ******************************************************************************/
57int rockchip_validate_power_state(unsigned int power_state,
58 psci_power_state_t *req_state)
59{
60 int pstate = psci_get_pstate_type(power_state);
61 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
62 int i;
63
64 assert(req_state);
65
66 if (pwr_lvl > PLAT_MAX_PWR_LVL)
67 return PSCI_E_INVALID_PARAMS;
68
69 /* Sanity check the requested state */
70 if (pstate == PSTATE_TYPE_STANDBY) {
71 /*
72 * It's probably to enter standby only on power level 0
73 * ignore any other power level.
74 */
75 if (pwr_lvl != MPIDR_AFFLVL0)
76 return PSCI_E_INVALID_PARAMS;
77
78 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
79 PLAT_MAX_RET_STATE;
80 } else {
81 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
82 req_state->pwr_domain_state[i] =
83 PLAT_MAX_OFF_STATE;
Tony Xie42e113e2016-07-16 11:16:51 +080084
85 for (i = (pwr_lvl + 1); i <= PLAT_MAX_PWR_LVL; i++)
86 req_state->pwr_domain_state[i] =
87 PLAT_MAX_RET_STATE;
Tony Xief6118cc2016-01-15 17:17:32 +080088 }
89
90 /* We expect the 'state id' to be zero */
91 if (psci_get_pstate_id(power_state))
92 return PSCI_E_INVALID_PARAMS;
93
94 return PSCI_E_SUCCESS;
95}
96
97void rockchip_get_sys_suspend_power_state(psci_power_state_t *req_state)
98{
99 int i;
100
101 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
102 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
103}
104
105/*******************************************************************************
106 * RockChip handler called when a CPU is about to enter standby.
107 ******************************************************************************/
108void rockchip_cpu_standby(plat_local_state_t cpu_state)
109{
110 unsigned int scr;
111
112 assert(cpu_state == PLAT_MAX_RET_STATE);
113
114 scr = read_scr_el3();
115 /* Enable PhysicalIRQ bit for NS world to wake the CPU */
116 write_scr_el3(scr | SCR_IRQ_BIT);
117 isb();
118 dsb();
119 wfi();
120
121 /*
122 * Restore SCR to the original value, synchronisation of scr_el3 is
123 * done by eret while el3_exit to save some execution cycles.
124 */
125 write_scr_el3(scr);
126}
127
128/*******************************************************************************
129 * RockChip handler called when a power domain is about to be turned on. The
130 * mpidr determines the CPU to be turned on.
131 ******************************************************************************/
132int rockchip_pwr_domain_on(u_register_t mpidr)
133{
134 if (rockchip_ops && rockchip_ops->cores_pwr_dm_on)
135 rockchip_ops->cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint);
136
137 return PSCI_E_SUCCESS;
138}
139
140/*******************************************************************************
141 * RockChip handler called when a power domain is about to be turned off. The
142 * target_state encodes the power state that each level should transition to.
143 ******************************************************************************/
144void rockchip_pwr_domain_off(const psci_power_state_t *target_state)
145{
Tony Xie42e113e2016-07-16 11:16:51 +0800146 uint32_t lvl;
147 plat_local_state_t lvl_state;
148
Tony Xief6118cc2016-01-15 17:17:32 +0800149 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
150
151 plat_rockchip_gic_cpuif_disable();
152
153 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
154 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800155
156 if (!rockchip_ops || !rockchip_ops->cores_pwr_dm_off)
157 return;
158
159 rockchip_ops->cores_pwr_dm_off();
160
161 if (!rockchip_ops->hlvl_pwr_dm_off)
162 return;
163
164 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
165 lvl_state = target_state->pwr_domain_state[lvl];
166 rockchip_ops->hlvl_pwr_dm_off(lvl, lvl_state);
167 }
Tony Xief6118cc2016-01-15 17:17:32 +0800168}
169
170/*******************************************************************************
171 * RockChip handler called when a power domain is about to be suspended. The
172 * target_state encodes the power state that each level should transition to.
173 ******************************************************************************/
174void rockchip_pwr_domain_suspend(const psci_power_state_t *target_state)
175{
Tony Xie42e113e2016-07-16 11:16:51 +0800176 uint32_t lvl;
177 plat_local_state_t lvl_state;
Tony Xief6118cc2016-01-15 17:17:32 +0800178
Tony Xie42e113e2016-07-16 11:16:51 +0800179 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
180 return;
Tony Xief6118cc2016-01-15 17:17:32 +0800181
Tony Xie42e113e2016-07-16 11:16:51 +0800182 if (rockchip_ops) {
183 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE &&
184 rockchip_ops->sys_pwr_dm_suspend) {
Tony Xief6118cc2016-01-15 17:17:32 +0800185 rockchip_ops->sys_pwr_dm_suspend();
Tony Xie42e113e2016-07-16 11:16:51 +0800186 } else if (rockchip_ops->cores_pwr_dm_suspend) {
Tony Xief6118cc2016-01-15 17:17:32 +0800187 rockchip_ops->cores_pwr_dm_suspend();
Tony Xie42e113e2016-07-16 11:16:51 +0800188 }
Tony Xief6118cc2016-01-15 17:17:32 +0800189 }
190
191 /* Prevent interrupts from spuriously waking up this cpu */
192 plat_rockchip_gic_cpuif_disable();
193
194 /* Perform the common cluster specific operations */
195 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
196 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800197
Tony Xie6d7d93c2016-09-02 11:13:38 -0700198 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
199 return;
200
Tony Xie42e113e2016-07-16 11:16:51 +0800201 if (!rockchip_ops || !rockchip_ops->hlvl_pwr_dm_suspend)
202 return;
203
204 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
205 lvl_state = target_state->pwr_domain_state[lvl];
206 rockchip_ops->hlvl_pwr_dm_suspend(lvl, lvl_state);
207 }
Tony Xief6118cc2016-01-15 17:17:32 +0800208}
209
210/*******************************************************************************
211 * RockChip handler called when a power domain has just been powered on after
212 * being turned off earlier. The target_state encodes the low power state that
213 * each level has woken up from.
214 ******************************************************************************/
215void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state)
216{
Tony Xie42e113e2016-07-16 11:16:51 +0800217 uint32_t lvl;
218 plat_local_state_t lvl_state;
219
Tony Xief6118cc2016-01-15 17:17:32 +0800220 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
221
Tony Xie42e113e2016-07-16 11:16:51 +0800222 if (!rockchip_ops)
223 goto comm_finish;
224
225 if (rockchip_ops->hlvl_pwr_dm_on_finish) {
226 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
227 lvl_state = target_state->pwr_domain_state[lvl];
228 rockchip_ops->hlvl_pwr_dm_on_finish(lvl, lvl_state);
229 }
230 }
231
232 if (rockchip_ops->cores_pwr_dm_on_finish)
Tony Xief6118cc2016-01-15 17:17:32 +0800233 rockchip_ops->cores_pwr_dm_on_finish();
Tony Xie42e113e2016-07-16 11:16:51 +0800234comm_finish:
Tony Xief6118cc2016-01-15 17:17:32 +0800235
236 /* Perform the common cluster specific operations */
237 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
238 /* Enable coherency if this cluster was off */
239 plat_cci_enable();
240 }
241
242 /* Enable the gic cpu interface */
243 plat_rockchip_gic_pcpu_init();
244
245 /* Program the gic per-cpu distributor or re-distributor interface */
246 plat_rockchip_gic_cpuif_enable();
247}
248
249/*******************************************************************************
250 * RockChip handler called when a power domain has just been powered on after
251 * having been suspended earlier. The target_state encodes the low power state
252 * that each level has woken up from.
253 * TODO: At the moment we reuse the on finisher and reinitialize the secure
254 * context. Need to implement a separate suspend finisher.
255 ******************************************************************************/
256void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
257{
Tony Xie42e113e2016-07-16 11:16:51 +0800258 uint32_t lvl;
259 plat_local_state_t lvl_state;
260
Tony Xief6118cc2016-01-15 17:17:32 +0800261 /* Nothing to be done on waking up from retention from CPU level */
Tony Xie42e113e2016-07-16 11:16:51 +0800262 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
Tony Xief6118cc2016-01-15 17:17:32 +0800263 return;
264
265 /* Perform system domain restore if woken up from system suspend */
Tony Xie42e113e2016-07-16 11:16:51 +0800266 if (!rockchip_ops)
267 goto comm_finish;
268
Tony Xie6d7d93c2016-09-02 11:13:38 -0700269 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
270 if (rockchip_ops->sys_pwr_dm_resume)
271 rockchip_ops->sys_pwr_dm_resume();
272 goto comm_finish;
273 }
274
Tony Xie42e113e2016-07-16 11:16:51 +0800275 if (rockchip_ops->hlvl_pwr_dm_resume) {
276 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
277 lvl_state = target_state->pwr_domain_state[lvl];
278 rockchip_ops->hlvl_pwr_dm_resume(lvl, lvl_state);
279 }
280 }
281
Tony Xie6d7d93c2016-09-02 11:13:38 -0700282 if (rockchip_ops->cores_pwr_dm_resume)
Tony Xie42e113e2016-07-16 11:16:51 +0800283 rockchip_ops->cores_pwr_dm_resume();
Tony Xie42e113e2016-07-16 11:16:51 +0800284 /*
Tony Xie6d7d93c2016-09-02 11:13:38 -0700285 * Program the gic per-cpu distributor or re-distributor interface.
Caesar Wangda9e6de2016-09-10 06:26:11 +0800286 * For sys power domain operation, resuming of the gic needs to operate
287 * in rockchip_ops->sys_pwr_dm_resume, according to the sys power mode
288 * implements.
289 */
Tony Xie42e113e2016-07-16 11:16:51 +0800290 plat_rockchip_gic_cpuif_enable();
Tony Xief6118cc2016-01-15 17:17:32 +0800291
Tony Xie6d7d93c2016-09-02 11:13:38 -0700292comm_finish:
Tony Xief6118cc2016-01-15 17:17:32 +0800293 /* Perform the common cluster specific operations */
294 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
295 /* Enable coherency if this cluster was off */
296 plat_cci_enable();
297 }
298}
299
300/*******************************************************************************
301 * RockChip handlers to reboot the system
302 ******************************************************************************/
303static void __dead2 rockchip_system_reset(void)
304{
305 assert(rockchip_ops && rockchip_ops->sys_gbl_soft_reset);
306
307 rockchip_ops->sys_gbl_soft_reset();
308}
309
310/*******************************************************************************
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800311 * RockChip handlers to power off the system
312 ******************************************************************************/
313static void __dead2 rockchip_system_poweroff(void)
314{
315 assert(rockchip_ops && rockchip_ops->system_off);
316
317 rockchip_ops->system_off();
318}
319
Caesar Wange67b1de2016-08-17 17:22:10 -0700320static void
321__dead2 rockchip_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
322{
323 if ((RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) &&
324 (rockchip_ops)) {
325 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE &&
326 rockchip_ops->sys_pwr_down_wfi)
327 rockchip_ops->sys_pwr_down_wfi(target_state);
328 }
329 psci_power_down_wfi();
330}
331
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800332/*******************************************************************************
Tony Xief6118cc2016-01-15 17:17:32 +0800333 * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
334 * standard
335 * platform layer will take care of registering the handlers with PSCI.
336 ******************************************************************************/
337const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
338 .cpu_standby = rockchip_cpu_standby,
339 .pwr_domain_on = rockchip_pwr_domain_on,
340 .pwr_domain_off = rockchip_pwr_domain_off,
341 .pwr_domain_suspend = rockchip_pwr_domain_suspend,
342 .pwr_domain_on_finish = rockchip_pwr_domain_on_finish,
343 .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish,
Caesar Wange67b1de2016-08-17 17:22:10 -0700344 .pwr_domain_pwr_down_wfi = rockchip_pwr_domain_pwr_down_wfi,
Tony Xief6118cc2016-01-15 17:17:32 +0800345 .system_reset = rockchip_system_reset,
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800346 .system_off = rockchip_system_poweroff,
Tony Xief6118cc2016-01-15 17:17:32 +0800347 .validate_power_state = rockchip_validate_power_state,
348 .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state
349};
350
351int plat_setup_psci_ops(uintptr_t sec_entrypoint,
352 const plat_psci_ops_t **psci_ops)
353{
354 *psci_ops = &plat_rockchip_psci_pm_ops;
355 rockchip_sec_entrypoint = sec_entrypoint;
356 return 0;
357}
358
Tony Xie42e113e2016-07-16 11:16:51 +0800359uintptr_t plat_get_sec_entrypoint(void)
360{
361 assert(rockchip_sec_entrypoint);
362 return rockchip_sec_entrypoint;
363}
364
Tony Xief6118cc2016-01-15 17:17:32 +0800365void plat_setup_rockchip_pm_ops(struct rockchip_pm_ops_cb *ops)
366{
367 rockchip_ops = ops;
368}