blob: fcd47a8c9fe1db75e5e148808607008c5a2cc23f [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
53static void plat_rockchip_sys_pwr_domain_resume(void)
54{
55 plat_rockchip_gic_init();
56 if (rockchip_ops && rockchip_ops->sys_pwr_dm_resume)
57 rockchip_ops->sys_pwr_dm_resume();
58}
59
60static void plat_rockchip_cores_pwr_domain_resume(void)
61{
62 if (rockchip_ops && rockchip_ops->cores_pwr_dm_resume)
63 rockchip_ops->cores_pwr_dm_resume();
64
65 /* Enable the gic cpu interface */
66 plat_rockchip_gic_pcpu_init();
67 /* Program the gic per-cpu distributor or re-distributor interface */
68 plat_rockchip_gic_cpuif_enable();
69}
70
71/*******************************************************************************
72 * Rockchip standard platform handler called to check the validity of the power
73 * state parameter.
74 ******************************************************************************/
75int rockchip_validate_power_state(unsigned int power_state,
76 psci_power_state_t *req_state)
77{
78 int pstate = psci_get_pstate_type(power_state);
79 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
80 int i;
81
82 assert(req_state);
83
84 if (pwr_lvl > PLAT_MAX_PWR_LVL)
85 return PSCI_E_INVALID_PARAMS;
86
87 /* Sanity check the requested state */
88 if (pstate == PSTATE_TYPE_STANDBY) {
89 /*
90 * It's probably to enter standby only on power level 0
91 * ignore any other power level.
92 */
93 if (pwr_lvl != MPIDR_AFFLVL0)
94 return PSCI_E_INVALID_PARAMS;
95
96 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
97 PLAT_MAX_RET_STATE;
98 } else {
99 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
100 req_state->pwr_domain_state[i] =
101 PLAT_MAX_OFF_STATE;
102 }
103
104 /* We expect the 'state id' to be zero */
105 if (psci_get_pstate_id(power_state))
106 return PSCI_E_INVALID_PARAMS;
107
108 return PSCI_E_SUCCESS;
109}
110
111void rockchip_get_sys_suspend_power_state(psci_power_state_t *req_state)
112{
113 int i;
114
115 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
116 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
117}
118
119/*******************************************************************************
120 * RockChip handler called when a CPU is about to enter standby.
121 ******************************************************************************/
122void rockchip_cpu_standby(plat_local_state_t cpu_state)
123{
124 unsigned int scr;
125
126 assert(cpu_state == PLAT_MAX_RET_STATE);
127
128 scr = read_scr_el3();
129 /* Enable PhysicalIRQ bit for NS world to wake the CPU */
130 write_scr_el3(scr | SCR_IRQ_BIT);
131 isb();
132 dsb();
133 wfi();
134
135 /*
136 * Restore SCR to the original value, synchronisation of scr_el3 is
137 * done by eret while el3_exit to save some execution cycles.
138 */
139 write_scr_el3(scr);
140}
141
142/*******************************************************************************
143 * RockChip handler called when a power domain is about to be turned on. The
144 * mpidr determines the CPU to be turned on.
145 ******************************************************************************/
146int rockchip_pwr_domain_on(u_register_t mpidr)
147{
148 if (rockchip_ops && rockchip_ops->cores_pwr_dm_on)
149 rockchip_ops->cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint);
150
151 return PSCI_E_SUCCESS;
152}
153
154/*******************************************************************************
155 * RockChip handler called when a power domain is about to be turned off. The
156 * target_state encodes the power state that each level should transition to.
157 ******************************************************************************/
158void rockchip_pwr_domain_off(const psci_power_state_t *target_state)
159{
160 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
161
162 plat_rockchip_gic_cpuif_disable();
163
164 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
165 plat_cci_disable();
166 if (rockchip_ops && rockchip_ops->cores_pwr_dm_off)
167 rockchip_ops->cores_pwr_dm_off();
168}
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{
176 if (RK_CORE_PWR_STATE(target_state) == PLAT_MAX_RET_STATE)
177 return;
178
179 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
180
181 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
182 if (rockchip_ops && rockchip_ops->sys_pwr_dm_suspend)
183 rockchip_ops->sys_pwr_dm_suspend();
184 } else {
185 if (rockchip_ops && rockchip_ops->cores_pwr_dm_suspend)
186 rockchip_ops->cores_pwr_dm_suspend();
187 }
188
189 /* Prevent interrupts from spuriously waking up this cpu */
190 plat_rockchip_gic_cpuif_disable();
191
192 /* Perform the common cluster specific operations */
193 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
194 plat_cci_disable();
195}
196
197/*******************************************************************************
198 * RockChip handler called when a power domain has just been powered on after
199 * being turned off earlier. The target_state encodes the low power state that
200 * each level has woken up from.
201 ******************************************************************************/
202void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state)
203{
204 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
205
206 if (rockchip_ops && rockchip_ops->cores_pwr_dm_on_finish)
207 rockchip_ops->cores_pwr_dm_on_finish();
208
209 /* Perform the common cluster specific operations */
210 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
211 /* Enable coherency if this cluster was off */
212 plat_cci_enable();
213 }
214
215 /* Enable the gic cpu interface */
216 plat_rockchip_gic_pcpu_init();
217
218 /* Program the gic per-cpu distributor or re-distributor interface */
219 plat_rockchip_gic_cpuif_enable();
220}
221
222/*******************************************************************************
223 * RockChip handler called when a power domain has just been powered on after
224 * having been suspended earlier. The target_state encodes the low power state
225 * that each level has woken up from.
226 * TODO: At the moment we reuse the on finisher and reinitialize the secure
227 * context. Need to implement a separate suspend finisher.
228 ******************************************************************************/
229void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
230{
231 /* Nothing to be done on waking up from retention from CPU level */
232 if (RK_CORE_PWR_STATE(target_state) == PLAT_MAX_RET_STATE)
233 return;
234
235 /* Perform system domain restore if woken up from system suspend */
236 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
237 plat_rockchip_sys_pwr_domain_resume();
238 else
239 plat_rockchip_cores_pwr_domain_resume();
240
241 /* Perform the common cluster specific operations */
242 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
243 /* Enable coherency if this cluster was off */
244 plat_cci_enable();
245 }
246}
247
248/*******************************************************************************
249 * RockChip handlers to reboot the system
250 ******************************************************************************/
251static void __dead2 rockchip_system_reset(void)
252{
253 assert(rockchip_ops && rockchip_ops->sys_gbl_soft_reset);
254
255 rockchip_ops->sys_gbl_soft_reset();
256}
257
258/*******************************************************************************
259 * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
260 * standard
261 * platform layer will take care of registering the handlers with PSCI.
262 ******************************************************************************/
263const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
264 .cpu_standby = rockchip_cpu_standby,
265 .pwr_domain_on = rockchip_pwr_domain_on,
266 .pwr_domain_off = rockchip_pwr_domain_off,
267 .pwr_domain_suspend = rockchip_pwr_domain_suspend,
268 .pwr_domain_on_finish = rockchip_pwr_domain_on_finish,
269 .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish,
270 .system_reset = rockchip_system_reset,
271 .validate_power_state = rockchip_validate_power_state,
272 .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state
273};
274
275int plat_setup_psci_ops(uintptr_t sec_entrypoint,
276 const plat_psci_ops_t **psci_ops)
277{
278 *psci_ops = &plat_rockchip_psci_pm_ops;
279 rockchip_sec_entrypoint = sec_entrypoint;
280 return 0;
281}
282
283void plat_setup_rockchip_pm_ops(struct rockchip_pm_ops_cb *ops)
284{
285 rockchip_ops = ops;
286}