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
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 */
83	pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING);
84
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	 */
126	pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0);
127}
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{
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
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();
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 ******************************************************************************/
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 */
249	pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
250			   PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM);
251
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 */
284	pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET,
285			   PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM);
286
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
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
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}
373