blob: a82f69677d95b63438855eb85185437e39ec09fc [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
Soren Brinkmann76fcae32016-03-06 20:16:27 -080030static int zynqmp_pwr_domain_on(u_register_t mpidr)
31{
32 unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr);
33 const struct pm_proc *proc;
34
35 VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
36
37 if (cpu_id == -1)
38 return PSCI_E_INTERN_FAIL;
39
40 proc = pm_get_proc(cpu_id);
Filip Drazicd7d62ce2017-02-07 12:03:56 +010041 /* Clear power down request */
42 pm_client_wakeup(proc);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080043
44 /* Send request to PMU to wake up selected APU CPU core */
Soren Brinkmann17aea222016-05-19 07:20:14 -070045 pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080046
47 return PSCI_E_SUCCESS;
48}
49
Soren Brinkmann76fcae32016-03-06 20:16:27 -080050static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state)
51{
52 unsigned int cpu_id = plat_my_core_pos();
53 const struct pm_proc *proc = pm_get_proc(cpu_id);
54
55 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
56 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
57 __func__, i, target_state->pwr_domain_state[i]);
58
59 /* Prevent interrupts from spuriously waking up this cpu */
60 gicv2_cpuif_disable();
61
62 /*
63 * Send request to PMU to power down the appropriate APU CPU
64 * core.
65 * According to PSCI specification, CPU_off function does not
66 * have resume address and CPU core can only be woken up
67 * invoking CPU_on function, during which resume address will
68 * be set.
69 */
Filip Drazic0bd9d0c2016-07-20 17:17:39 +020070 pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080071}
72
Soren Brinkmann76fcae32016-03-06 20:16:27 -080073static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
74{
Filip Drazic0bd9d0c2016-07-20 17:17:39 +020075 unsigned int state;
Soren Brinkmann76fcae32016-03-06 20:16:27 -080076 unsigned int cpu_id = plat_my_core_pos();
77 const struct pm_proc *proc = pm_get_proc(cpu_id);
78
79 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
80 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
81 __func__, i, target_state->pwr_domain_state[i]);
82
Filip Drazic0bd9d0c2016-07-20 17:17:39 +020083 state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
84 PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
85
Soren Brinkmann76fcae32016-03-06 20:16:27 -080086 /* Send request to PMU to suspend this core */
Filip Drazic0bd9d0c2016-07-20 17:17:39 +020087 pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry);
Soren Brinkmann76fcae32016-03-06 20:16:27 -080088
89 /* APU is to be turned off */
90 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
Soren Brinkmann76fcae32016-03-06 20:16:27 -080091 /* disable coherency */
92 plat_arm_interconnect_exit_coherency();
93 }
94}
95
96static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state)
97{
98 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++)
99 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
100 __func__, i, target_state->pwr_domain_state[i]);
101
102 gicv2_cpuif_enable();
103 gicv2_pcpu_distif_init();
104}
105
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800106static void zynqmp_pwr_domain_suspend_finish(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 /* Clear the APU power control register for this cpu */
116 pm_client_wakeup(proc);
117
118 /* enable coherency */
119 plat_arm_interconnect_enter_coherency();
Soren Brinkmann3b6ebcb2016-02-18 21:16:35 -0800120 /* APU was turned off */
121 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
122 plat_arm_gic_init();
123 } else {
124 gicv2_cpuif_enable();
125 gicv2_pcpu_distif_init();
126 }
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800127}
128
129/*******************************************************************************
130 * ZynqMP handlers to shutdown/reboot the system
131 ******************************************************************************/
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800132
133static void __dead2 zynqmp_system_off(void)
134{
135 /* disable coherency */
136 plat_arm_interconnect_exit_coherency();
137
138 /* Send the power down request to the PMU */
Soren Brinkmann58fbb9b2016-09-02 09:50:54 -0700139 pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
Siva Durga Prasad Paladugu1f80d3f2018-04-30 15:56:10 +0530140 pm_get_shutdown_scope());
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800141
142 while (1)
143 wfi();
144}
145
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800146static void __dead2 zynqmp_system_reset(void)
147{
148 /* disable coherency */
149 plat_arm_interconnect_exit_coherency();
150
151 /* Send the system reset request to the PMU */
Soren Brinkmann58fbb9b2016-09-02 09:50:54 -0700152 pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET,
Siva Durga Prasad Paladugu1f80d3f2018-04-30 15:56:10 +0530153 pm_get_shutdown_scope());
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800154
155 while (1)
156 wfi();
157}
158
159int zynqmp_validate_power_state(unsigned int power_state,
160 psci_power_state_t *req_state)
161{
162 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
163
Stefan Krsmanovic3779c5c2016-05-09 18:00:47 +0200164 int pstate = psci_get_pstate_type(power_state);
165
166 assert(req_state);
167
168 /* Sanity check the requested state */
169 if (pstate == PSTATE_TYPE_STANDBY)
170 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
171 else
172 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
173
174 /* We expect the 'state id' to be zero */
175 if (psci_get_pstate_id(power_state))
176 return PSCI_E_INVALID_PARAMS;
177
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800178 return PSCI_E_SUCCESS;
179}
180
181int zynqmp_validate_ns_entrypoint(unsigned long ns_entrypoint)
182{
183 VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint);
184
185 /* FIXME: Actually validate */
186 return PSCI_E_SUCCESS;
187}
188
189void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state)
190{
191 req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
192 req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
193}
194
195/*******************************************************************************
196 * Export the platform handlers to enable psci to invoke them
197 ******************************************************************************/
198static const struct plat_psci_ops zynqmp_psci_ops = {
199 .cpu_standby = zynqmp_cpu_standby,
200 .pwr_domain_on = zynqmp_pwr_domain_on,
201 .pwr_domain_off = zynqmp_pwr_domain_off,
202 .pwr_domain_suspend = zynqmp_pwr_domain_suspend,
203 .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish,
204 .pwr_domain_suspend_finish = zynqmp_pwr_domain_suspend_finish,
205 .system_off = zynqmp_system_off,
206 .system_reset = zynqmp_system_reset,
207 .validate_power_state = zynqmp_validate_power_state,
208 .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint,
209 .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state,
210};
211
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800212/*******************************************************************************
213 * Export the platform specific power ops.
214 ******************************************************************************/
215int plat_setup_psci_ops(uintptr_t sec_entrypoint,
216 const struct plat_psci_ops **psci_ops)
217{
218 zynqmp_sec_entry = sec_entrypoint;
219
Siva Durga Prasad Paladugu40808bc2018-04-30 19:43:03 +0530220 *psci_ops = &zynqmp_psci_ops;
Soren Brinkmann76fcae32016-03-06 20:16:27 -0800221
222 return 0;
223}