blob: c9fd36113e1f425f8d287f34b04389d0566a39ee [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);
81
82 /* Send request to PMU to wake up selected APU CPU core */
Soren Brinkmann17aea222016-05-19 07:20:14 -070083 pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080084
85 return PSCI_E_SUCCESS;
86}
87
88static void zynqmp_nopmu_pwr_domain_off(const psci_power_state_t *target_state)
89{
90 uint32_t r;
91 unsigned int cpu_id = plat_my_core_pos();
92
93 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
94 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
95 __func__, i, target_state->pwr_domain_state[i]);
96
97 /* Prevent interrupts from spuriously waking up this cpu */
98 gicv2_cpuif_disable();
99
100 /* set power down request */
101 r = mmio_read_32(APU_PWRCTL);
102 r |= (1 << cpu_id);
103 mmio_write_32(APU_PWRCTL, r);
104}
105
106static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state)
107{
108 unsigned int cpu_id = plat_my_core_pos();
109 const struct pm_proc *proc = pm_get_proc(cpu_id);
110
111 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
112 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
113 __func__, i, target_state->pwr_domain_state[i]);
114
115 /* Prevent interrupts from spuriously waking up this cpu */
116 gicv2_cpuif_disable();
117
118 /*
119 * Send request to PMU to power down the appropriate APU CPU
120 * core.
121 * According to PSCI specification, CPU_off function does not
122 * have resume address and CPU core can only be woken up
123 * invoking CPU_on function, during which resume address will
124 * be set.
125 */
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200126 pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800127}
128
129static void zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t *target_state)
130{
131 uint32_t r;
132 unsigned int cpu_id = plat_my_core_pos();
133
134 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
135 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
136 __func__, i, target_state->pwr_domain_state[i]);
137
138 /* set power down request */
139 r = mmio_read_32(APU_PWRCTL);
140 r |= (1 << cpu_id);
141 mmio_write_32(APU_PWRCTL, r);
142
143 /* program RVBAR */
144 mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry);
145 mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32);
146
147 /* clear VINITHI */
148 r = mmio_read_32(APU_CONFIG_0);
149 r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id);
150 mmio_write_32(APU_CONFIG_0, r);
151
152 /* enable power up on IRQ */
153 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id);
154}
155
156static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
157{
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200158 unsigned int state;
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800159 unsigned int cpu_id = plat_my_core_pos();
160 const struct pm_proc *proc = pm_get_proc(cpu_id);
161
162 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
163 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
164 __func__, i, target_state->pwr_domain_state[i]);
165
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200166 state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
167 PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
168
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800169 /* Send request to PMU to suspend this core */
Filip Drazic0bd9d0c2016-07-20 17:17:39 +0200170 pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800171
172 /* APU is to be turned off */
173 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800174 /* disable coherency */
175 plat_arm_interconnect_exit_coherency();
176 }
177}
178
179static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state)
180{
181 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
182 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
183 __func__, i, target_state->pwr_domain_state[i]);
184
185 gicv2_cpuif_enable();
186 gicv2_pcpu_distif_init();
187}
188
189static void zynqmp_nopmu_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
190{
191 uint32_t r;
192 unsigned int cpu_id = plat_my_core_pos();
193
194 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
195 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
196 __func__, i, target_state->pwr_domain_state[i]);
197
198 /* disable power up on IRQ */
199 mmio_write_32(PMU_GLOBAL_REQ_PWRUP_DIS, 1 << cpu_id);
200
201 /* clear powerdown bit */
202 r = mmio_read_32(APU_PWRCTL);
203 r &= ~(1 << cpu_id);
204 mmio_write_32(APU_PWRCTL, r);
205}
206
207static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
208{
209 unsigned int cpu_id = plat_my_core_pos();
210 const struct pm_proc *proc = pm_get_proc(cpu_id);
211
212 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
213 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
214 __func__, i, target_state->pwr_domain_state[i]);
215
216 /* Clear the APU power control register for this cpu */
217 pm_client_wakeup(proc);
218
219 /* enable coherency */
220 plat_arm_interconnect_enter_coherency();
Soren Brinkmann3b6ebcb2016-02-18 21:16:35 -0800221 /* APU was turned off */
222 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
223 plat_arm_gic_init();
224 } else {
225 gicv2_cpuif_enable();
226 gicv2_pcpu_distif_init();
227 }
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800228}
229
230/*******************************************************************************
231 * ZynqMP handlers to shutdown/reboot the system
232 ******************************************************************************/
233static void __dead2 zynqmp_nopmu_system_off(void)
234{
235 ERROR("ZynqMP System Off: operation not handled.\n");
236
237 /* disable coherency */
238 plat_arm_interconnect_exit_coherency();
239
240 panic();
241}
242
243static void __dead2 zynqmp_system_off(void)
244{
245 /* disable coherency */
246 plat_arm_interconnect_exit_coherency();
247
248 /* Send the power down request to the PMU */
Soren Brinkmann58fbb9b2016-09-02 09:50:54 -0700249 pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
250 PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800251
252 while (1)
253 wfi();
254}
255
256static void __dead2 zynqmp_nopmu_system_reset(void)
257{
258 /*
259 * This currently triggers a system reset. I.e. the whole
260 * system will be reset! Including RPUs, PMU, PL, etc.
261 */
262
263 /* disable coherency */
264 plat_arm_interconnect_exit_coherency();
265
266 /* bypass RPLL (needed on 1.0 silicon) */
267 uint32_t reg = mmio_read_32(CRL_APB_RPLL_CTRL);
268 reg |= CRL_APB_RPLL_CTRL_BYPASS;
269 mmio_write_32(CRL_APB_RPLL_CTRL, reg);
270
271 /* trigger system reset */
272 mmio_write_32(CRL_APB_RESET_CTRL, CRL_APB_RESET_CTRL_SOFT_RESET);
273
274 while (1)
275 wfi();
276}
277
278static void __dead2 zynqmp_system_reset(void)
279{
280 /* disable coherency */
281 plat_arm_interconnect_exit_coherency();
282
283 /* Send the system reset request to the PMU */
Soren Brinkmann58fbb9b2016-09-02 09:50:54 -0700284 pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET,
285 PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM);
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800286
287 while (1)
288 wfi();
289}
290
291int zynqmp_validate_power_state(unsigned int power_state,
292 psci_power_state_t *req_state)
293{
294 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
295
Stefan Krsmanovic3779c5c2016-05-09 18:00:47 +0200296 int pstate = psci_get_pstate_type(power_state);
297
298 assert(req_state);
299
300 /* Sanity check the requested state */
301 if (pstate == PSTATE_TYPE_STANDBY)
302 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
303 else
304 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
305
306 /* We expect the 'state id' to be zero */
307 if (psci_get_pstate_id(power_state))
308 return PSCI_E_INVALID_PARAMS;
309
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800310 return PSCI_E_SUCCESS;
311}
312
313int zynqmp_validate_ns_entrypoint(unsigned long ns_entrypoint)
314{
315 VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint);
316
317 /* FIXME: Actually validate */
318 return PSCI_E_SUCCESS;
319}
320
321void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state)
322{
323 req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
324 req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
325}
326
327/*******************************************************************************
328 * Export the platform handlers to enable psci to invoke them
329 ******************************************************************************/
330static const struct plat_psci_ops zynqmp_psci_ops = {
331 .cpu_standby = zynqmp_cpu_standby,
332 .pwr_domain_on = zynqmp_pwr_domain_on,
333 .pwr_domain_off = zynqmp_pwr_domain_off,
334 .pwr_domain_suspend = zynqmp_pwr_domain_suspend,
335 .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish,
336 .pwr_domain_suspend_finish = zynqmp_pwr_domain_suspend_finish,
337 .system_off = zynqmp_system_off,
338 .system_reset = zynqmp_system_reset,
339 .validate_power_state = zynqmp_validate_power_state,
340 .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint,
341 .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state,
342};
343
344static const struct plat_psci_ops zynqmp_nopmu_psci_ops = {
345 .cpu_standby = zynqmp_cpu_standby,
346 .pwr_domain_on = zynqmp_nopmu_pwr_domain_on,
347 .pwr_domain_off = zynqmp_nopmu_pwr_domain_off,
348 .pwr_domain_suspend = zynqmp_nopmu_pwr_domain_suspend,
349 .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish,
350 .pwr_domain_suspend_finish = zynqmp_nopmu_pwr_domain_suspend_finish,
351 .system_off = zynqmp_nopmu_system_off,
352 .system_reset = zynqmp_nopmu_system_reset,
353 .validate_power_state = zynqmp_validate_power_state,
354 .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint,
355 .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state,
356};
357
358/*******************************************************************************
359 * Export the platform specific power ops.
360 ******************************************************************************/
361int plat_setup_psci_ops(uintptr_t sec_entrypoint,
362 const struct plat_psci_ops **psci_ops)
363{
364 zynqmp_sec_entry = sec_entrypoint;
365
366 if (zynqmp_is_pmu_up())
367 *psci_ops = &zynqmp_psci_ops;
368 else
369 *psci_ops = &zynqmp_nopmu_psci_ops;
370
371 return 0;
372}