blob: d0df6a87144b797e6863d48e9168b55d47d9d3a7 [file] [log] [blame]
Soren Brinkmann76fcae32016-03-06 20:16:27 -08001/*
2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Soren Brinkmann76fcae32016-03-06 20:16:27 -08005 */
6
7#include <arch_helpers.h>
Soren Brinkmann76fcae32016-03-06 20:16:27 -08008#include <assert.h>
9#include <debug.h>
Isla Mitchelle3631462017-07-14 10:46:32 +010010#include <errno.h>
Soren Brinkmann76fcae32016-03-06 20:16:27 -080011#include <gicv2.h>
12#include <mmio.h>
13#include <plat_arm.h>
14#include <platform.h>
15#include <psci.h>
16#include "pm_api_sys.h"
17#include "pm_client.h"
18#include "zynqmp_private.h"
19
20uintptr_t zynqmp_sec_entry;
21
22void zynqmp_cpu_standby(plat_local_state_t cpu_state)
23{
24 VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state);
25
26 dsb();
27 wfi();
28}
29
30static int zynqmp_nopmu_pwr_domain_on(u_register_t mpidr)
31{
32 uint32_t r;
33 unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr);
34
35 VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
36
37 if (cpu_id == -1)
38 return PSCI_E_INTERN_FAIL;
39
40 /* program RVBAR */
41 mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry);
42 mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32);
43
44 /* clear VINITHI */
45 r = mmio_read_32(APU_CONFIG_0);
46 r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id);
47 mmio_write_32(APU_CONFIG_0, r);
48
49 /* clear power down request */
50 r = mmio_read_32(APU_PWRCTL);
51 r &= ~(1 << cpu_id);
52 mmio_write_32(APU_PWRCTL, r);
53
54 /* power up island */
55 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id);
56 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_TRIG, 1 << cpu_id);
57 /* FIXME: we should have a way to break out */
58 while (mmio_read_32(PMU_GLOBAL_REQ_PWRUP_STATUS) & (1 << cpu_id))
59 ;
60
61 /* release core reset */
62 r = mmio_read_32(CRF_APB_RST_FPD_APU);
63 r &= ~((CRF_APB_RST_FPD_APU_ACPU_PWRON_RESET |
64 CRF_APB_RST_FPD_APU_ACPU_RESET) << cpu_id);
65 mmio_write_32(CRF_APB_RST_FPD_APU, r);
66
67 return PSCI_E_SUCCESS;
68}
69
70static int zynqmp_pwr_domain_on(u_register_t mpidr)
71{
72 unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr);
73 const struct pm_proc *proc;
74
75 VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
76
77 if (cpu_id == -1)
78 return PSCI_E_INTERN_FAIL;
79
80 proc = pm_get_proc(cpu_id);
Filip Drazicd7d62ce2017-02-07 12:03:56 +010081 /* Clear power down request */
82 pm_client_wakeup(proc);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080083
84 /* Send request to PMU to wake up selected APU CPU core */
Soren Brinkmann17aea222016-05-19 07:20:14 -070085 pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080086
87 return PSCI_E_SUCCESS;
88}
89
90static void zynqmp_nopmu_pwr_domain_off(const psci_power_state_t *target_state)
91{
92 uint32_t r;
93 unsigned int cpu_id = plat_my_core_pos();
94
95 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
96 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
97 __func__, i, target_state->pwr_domain_state[i]);
98
99 /* Prevent interrupts from spuriously waking up this cpu */
100 gicv2_cpuif_disable();
101
102 /* set power down request */
103 r = mmio_read_32(APU_PWRCTL);
104 r |= (1 << cpu_id);
105 mmio_write_32(APU_PWRCTL, r);
106}
107
108static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state)
109{
110 unsigned int cpu_id = plat_my_core_pos();
111 const struct pm_proc *proc = pm_get_proc(cpu_id);
112
113 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
114 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
115 __func__, i, target_state->pwr_domain_state[i]);
116
117 /* Prevent interrupts from spuriously waking up this cpu */
118 gicv2_cpuif_disable();
119
120 /*
121 * Send request to PMU to power down the appropriate APU CPU
122 * core.
123 * According to PSCI specification, CPU_off function does not
124 * have resume address and CPU core can only be woken up
125 * invoking CPU_on function, during which resume address will
126 * be set.
127 */
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200128 pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800129}
130
131static void zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t *target_state)
132{
133 uint32_t r;
134 unsigned int cpu_id = plat_my_core_pos();
135
136 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
137 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
138 __func__, i, target_state->pwr_domain_state[i]);
139
140 /* set power down request */
141 r = mmio_read_32(APU_PWRCTL);
142 r |= (1 << cpu_id);
143 mmio_write_32(APU_PWRCTL, r);
144
145 /* program RVBAR */
146 mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry);
147 mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32);
148
149 /* clear VINITHI */
150 r = mmio_read_32(APU_CONFIG_0);
151 r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id);
152 mmio_write_32(APU_CONFIG_0, r);
153
154 /* enable power up on IRQ */
155 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id);
156}
157
158static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
159{
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200160 unsigned int state;
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800161 unsigned int cpu_id = plat_my_core_pos();
162 const struct pm_proc *proc = pm_get_proc(cpu_id);
163
164 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
165 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
166 __func__, i, target_state->pwr_domain_state[i]);
167
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200168 state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
169 PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
170
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800171 /* Send request to PMU to suspend this core */
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200172 pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800173
174 /* APU is to be turned off */
175 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800176 /* disable coherency */
177 plat_arm_interconnect_exit_coherency();
178 }
179}
180
181static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state)
182{
183 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
184 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
185 __func__, i, target_state->pwr_domain_state[i]);
186
187 gicv2_cpuif_enable();
188 gicv2_pcpu_distif_init();
189}
190
191static void zynqmp_nopmu_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
192{
193 uint32_t r;
194 unsigned int cpu_id = plat_my_core_pos();
195
196 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
197 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
198 __func__, i, target_state->pwr_domain_state[i]);
199
200 /* disable power up on IRQ */
201 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_DIS, 1 << cpu_id);
202
203 /* clear powerdown bit */
204 r = mmio_read_32(APU_PWRCTL);
205 r &= ~(1 << cpu_id);
206 mmio_write_32(APU_PWRCTL, r);
207}
208
209static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
210{
211 unsigned int cpu_id = plat_my_core_pos();
212 const struct pm_proc *proc = pm_get_proc(cpu_id);
213
214 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
215 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
216 __func__, i, target_state->pwr_domain_state[i]);
217
218 /* Clear the APU power control register for this cpu */
219 pm_client_wakeup(proc);
220
221 /* enable coherency */
222 plat_arm_interconnect_enter_coherency();
Soren Brinkmann3b6ebcb2016-02-18 21:16:35 -0800223 /* APU was turned off */
224 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
225 plat_arm_gic_init();
226 } else {
227 gicv2_cpuif_enable();
228 gicv2_pcpu_distif_init();
229 }
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800230}
231
232/*******************************************************************************
233 * ZynqMP handlers to shutdown/reboot the system
234 ******************************************************************************/
235static void __dead2 zynqmp_nopmu_system_off(void)
236{
237 ERROR("ZynqMP System Off: operation not handled.\n");
238
239 /* disable coherency */
240 plat_arm_interconnect_exit_coherency();
241
242 panic();
243}
244
245static void __dead2 zynqmp_system_off(void)
246{
247 /* disable coherency */
248 plat_arm_interconnect_exit_coherency();
249
250 /* Send the power down request to the PMU */
Soren Brinkmann58fbb9b2016-09-02 09:50:54 -0700251 pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
Siva Durga Prasad Paladugu1f80d3f2018-04-30 15:56:10 +0530252 pm_get_shutdown_scope());
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800253
254 while (1)
255 wfi();
256}
257
258static void __dead2 zynqmp_nopmu_system_reset(void)
259{
260 /*
261 * This currently triggers a system reset. I.e. the whole
262 * system will be reset! Including RPUs, PMU, PL, etc.
263 */
264
265 /* disable coherency */
266 plat_arm_interconnect_exit_coherency();
267
268 /* bypass RPLL (needed on 1.0 silicon) */
269 uint32_t reg = mmio_read_32(CRL_APB_RPLL_CTRL);
270 reg |= CRL_APB_RPLL_CTRL_BYPASS;
271 mmio_write_32(CRL_APB_RPLL_CTRL, reg);
272
273 /* trigger system reset */
274 mmio_write_32(CRL_APB_RESET_CTRL, CRL_APB_RESET_CTRL_SOFT_RESET);
275
276 while (1)
277 wfi();
278}
279
280static void __dead2 zynqmp_system_reset(void)
281{
282 /* disable coherency */
283 plat_arm_interconnect_exit_coherency();
284
285 /* Send the system reset request to the PMU */
Soren Brinkmann58fbb9b2016-09-02 09:50:54 -0700286 pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET,
Siva Durga Prasad Paladugu1f80d3f2018-04-30 15:56:10 +0530287 pm_get_shutdown_scope());
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800288
289 while (1)
290 wfi();
291}
292
293int zynqmp_validate_power_state(unsigned int power_state,
294 psci_power_state_t *req_state)
295{
296 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
297
Stefan Krsmanovic3779c5c2016-05-09 18:00:47 +0200298 int pstate = psci_get_pstate_type(power_state);
299
300 assert(req_state);
301
302 /* Sanity check the requested state */
303 if (pstate == PSTATE_TYPE_STANDBY)
304 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
305 else
306 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
307
308 /* We expect the 'state id' to be zero */
309 if (psci_get_pstate_id(power_state))
310 return PSCI_E_INVALID_PARAMS;
311
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800312 return PSCI_E_SUCCESS;
313}
314
315int zynqmp_validate_ns_entrypoint(unsigned long ns_entrypoint)
316{
317 VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint);
318
319 /* FIXME: Actually validate */
320 return PSCI_E_SUCCESS;
321}
322
323void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state)
324{
325 req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
326 req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
327}
328
329/*******************************************************************************
330 * Export the platform handlers to enable psci to invoke them
331 ******************************************************************************/
332static const struct plat_psci_ops zynqmp_psci_ops = {
333 .cpu_standby = zynqmp_cpu_standby,
334 .pwr_domain_on = zynqmp_pwr_domain_on,
335 .pwr_domain_off = zynqmp_pwr_domain_off,
336 .pwr_domain_suspend = zynqmp_pwr_domain_suspend,
337 .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish,
338 .pwr_domain_suspend_finish = zynqmp_pwr_domain_suspend_finish,
339 .system_off = zynqmp_system_off,
340 .system_reset = zynqmp_system_reset,
341 .validate_power_state = zynqmp_validate_power_state,
342 .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint,
343 .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state,
344};
345
346static const struct plat_psci_ops zynqmp_nopmu_psci_ops = {
347 .cpu_standby = zynqmp_cpu_standby,
348 .pwr_domain_on = zynqmp_nopmu_pwr_domain_on,
349 .pwr_domain_off = zynqmp_nopmu_pwr_domain_off,
350 .pwr_domain_suspend = zynqmp_nopmu_pwr_domain_suspend,
351 .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish,
352 .pwr_domain_suspend_finish = zynqmp_nopmu_pwr_domain_suspend_finish,
353 .system_off = zynqmp_nopmu_system_off,
354 .system_reset = zynqmp_nopmu_system_reset,
355 .validate_power_state = zynqmp_validate_power_state,
356 .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint,
357 .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state,
358};
359
360/*******************************************************************************
361 * Export the platform specific power ops.
362 ******************************************************************************/
363int plat_setup_psci_ops(uintptr_t sec_entrypoint,
364 const struct plat_psci_ops **psci_ops)
365{
366 zynqmp_sec_entry = sec_entrypoint;
367
368 if (zynqmp_is_pmu_up())
369 *psci_ops = &zynqmp_psci_ops;
370 else
371 *psci_ops = &zynqmp_nopmu_psci_ops;
372
373 return 0;
374}