1 /*
2  * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <arch_helpers.h>
8 #include <assert.h>
9 #include <debug.h>
10 #include <errno.h>
11 #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 
20 uintptr_t zynqmp_sec_entry;
21 
zynqmp_cpu_standby(plat_local_state_t cpu_state)22 void 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 
zynqmp_nopmu_pwr_domain_on(u_register_t mpidr)30 static 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 
zynqmp_pwr_domain_on(u_register_t mpidr)70 static 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 */
83 	pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING);
84 
85 	return PSCI_E_SUCCESS;
86 }
87 
zynqmp_nopmu_pwr_domain_off(const psci_power_state_t * target_state)88 static 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 
zynqmp_pwr_domain_off(const psci_power_state_t * target_state)106 static 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 	 */
126 	pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
127 }
128 
zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t * target_state)129 static 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 
zynqmp_pwr_domain_suspend(const psci_power_state_t * target_state)156 static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
157 {
158 	unsigned int state;
159 	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 
166 	state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
167 		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
168 
169 	/* Send request to PMU to suspend this core */
170 	pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry);
171 
172 	/* APU is to be turned off */
173 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
174 		/* disable coherency */
175 		plat_arm_interconnect_exit_coherency();
176 	}
177 }
178 
zynqmp_pwr_domain_on_finish(const psci_power_state_t * target_state)179 static 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 
zynqmp_nopmu_pwr_domain_suspend_finish(const psci_power_state_t * target_state)189 static 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 
zynqmp_pwr_domain_suspend_finish(const psci_power_state_t * target_state)207 static 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();
221 	/* 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 	}
228 }
229 
230 /*******************************************************************************
231  * ZynqMP handlers to shutdown/reboot the system
232  ******************************************************************************/
zynqmp_nopmu_system_off(void)233 static 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 
zynqmp_system_off(void)243 static 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 */
249 	pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
250 			   PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM);
251 
252 	while (1)
253 		wfi();
254 }
255 
zynqmp_nopmu_system_reset(void)256 static 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 
zynqmp_system_reset(void)278 static 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 */
284 	pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET,
285 			   PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM);
286 
287 	while (1)
288 		wfi();
289 }
290 
zynqmp_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)291 int 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 
296 	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 
310 	return PSCI_E_SUCCESS;
311 }
312 
zynqmp_validate_ns_entrypoint(unsigned long ns_entrypoint)313 int 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 
zynqmp_get_sys_suspend_power_state(psci_power_state_t * req_state)321 void 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  ******************************************************************************/
330 static 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 
344 static 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  ******************************************************************************/
plat_setup_psci_ops(uintptr_t sec_entrypoint,const struct plat_psci_ops ** psci_ops)361 int 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 }
373