xref: /rk3399_ARM-atf/plat/arm/board/fvp/fvp_pm.c (revision 3fc4124c7513554dc14a83a0b94010ce771e7e1c)
1*3fc4124cSDan Handley /*
2*3fc4124cSDan Handley  * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved.
3*3fc4124cSDan Handley  *
4*3fc4124cSDan Handley  * Redistribution and use in source and binary forms, with or without
5*3fc4124cSDan Handley  * modification, are permitted provided that the following conditions are met:
6*3fc4124cSDan Handley  *
7*3fc4124cSDan Handley  * Redistributions of source code must retain the above copyright notice, this
8*3fc4124cSDan Handley  * list of conditions and the following disclaimer.
9*3fc4124cSDan Handley  *
10*3fc4124cSDan Handley  * Redistributions in binary form must reproduce the above copyright notice,
11*3fc4124cSDan Handley  * this list of conditions and the following disclaimer in the documentation
12*3fc4124cSDan Handley  * and/or other materials provided with the distribution.
13*3fc4124cSDan Handley  *
14*3fc4124cSDan Handley  * Neither the name of ARM nor the names of its contributors may be used
15*3fc4124cSDan Handley  * to endorse or promote products derived from this software without specific
16*3fc4124cSDan Handley  * prior written permission.
17*3fc4124cSDan Handley  *
18*3fc4124cSDan Handley  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19*3fc4124cSDan Handley  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*3fc4124cSDan Handley  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*3fc4124cSDan Handley  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22*3fc4124cSDan Handley  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23*3fc4124cSDan Handley  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24*3fc4124cSDan Handley  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25*3fc4124cSDan Handley  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26*3fc4124cSDan Handley  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27*3fc4124cSDan Handley  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28*3fc4124cSDan Handley  * POSSIBILITY OF SUCH DAMAGE.
29*3fc4124cSDan Handley  */
30*3fc4124cSDan Handley 
31*3fc4124cSDan Handley #include <arch_helpers.h>
32*3fc4124cSDan Handley #include <arm_config.h>
33*3fc4124cSDan Handley #include <arm_gic.h>
34*3fc4124cSDan Handley #include <assert.h>
35*3fc4124cSDan Handley #include <debug.h>
36*3fc4124cSDan Handley #include <errno.h>
37*3fc4124cSDan Handley #include <mmio.h>
38*3fc4124cSDan Handley #include <platform.h>
39*3fc4124cSDan Handley #include <plat_arm.h>
40*3fc4124cSDan Handley #include <psci.h>
41*3fc4124cSDan Handley #include <v2m_def.h>
42*3fc4124cSDan Handley #include "drivers/pwrc/fvp_pwrc.h"
43*3fc4124cSDan Handley #include "fvp_def.h"
44*3fc4124cSDan Handley #include "fvp_private.h"
45*3fc4124cSDan Handley 
46*3fc4124cSDan Handley 
47*3fc4124cSDan Handley typedef volatile struct mailbox {
48*3fc4124cSDan Handley 	unsigned long value __aligned(CACHE_WRITEBACK_GRANULE);
49*3fc4124cSDan Handley } mailbox_t;
50*3fc4124cSDan Handley 
51*3fc4124cSDan Handley /*******************************************************************************
52*3fc4124cSDan Handley  * Private FVP function to program the mailbox for a cpu before it is released
53*3fc4124cSDan Handley  * from reset.
54*3fc4124cSDan Handley  ******************************************************************************/
55*3fc4124cSDan Handley static void fvp_program_mailbox(uint64_t mpidr, uint64_t address)
56*3fc4124cSDan Handley {
57*3fc4124cSDan Handley 	uint64_t linear_id;
58*3fc4124cSDan Handley 	mailbox_t *fvp_mboxes;
59*3fc4124cSDan Handley 
60*3fc4124cSDan Handley 	linear_id = platform_get_core_pos(mpidr);
61*3fc4124cSDan Handley 	fvp_mboxes = (mailbox_t *)MBOX_BASE;
62*3fc4124cSDan Handley 	fvp_mboxes[linear_id].value = address;
63*3fc4124cSDan Handley 	flush_dcache_range((unsigned long) &fvp_mboxes[linear_id],
64*3fc4124cSDan Handley 			   sizeof(unsigned long));
65*3fc4124cSDan Handley }
66*3fc4124cSDan Handley 
67*3fc4124cSDan Handley /*******************************************************************************
68*3fc4124cSDan Handley  * Function which implements the common FVP specific operations to power down a
69*3fc4124cSDan Handley  * cpu in response to a CPU_OFF or CPU_SUSPEND request.
70*3fc4124cSDan Handley  ******************************************************************************/
71*3fc4124cSDan Handley static void fvp_cpu_pwrdwn_common(void)
72*3fc4124cSDan Handley {
73*3fc4124cSDan Handley 	/* Prevent interrupts from spuriously waking up this cpu */
74*3fc4124cSDan Handley 	arm_gic_cpuif_deactivate();
75*3fc4124cSDan Handley 
76*3fc4124cSDan Handley 	/* Program the power controller to power off this cpu. */
77*3fc4124cSDan Handley 	fvp_pwrc_write_ppoffr(read_mpidr_el1());
78*3fc4124cSDan Handley }
79*3fc4124cSDan Handley 
80*3fc4124cSDan Handley /*******************************************************************************
81*3fc4124cSDan Handley  * Function which implements the common FVP specific operations to power down a
82*3fc4124cSDan Handley  * cluster in response to a CPU_OFF or CPU_SUSPEND request.
83*3fc4124cSDan Handley  ******************************************************************************/
84*3fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void)
85*3fc4124cSDan Handley {
86*3fc4124cSDan Handley 	uint64_t mpidr = read_mpidr_el1();
87*3fc4124cSDan Handley 
88*3fc4124cSDan Handley 	/* Disable coherency if this cluster is to be turned off */
89*3fc4124cSDan Handley 	fvp_cci_disable();
90*3fc4124cSDan Handley 
91*3fc4124cSDan Handley 	/* Program the power controller to turn the cluster off */
92*3fc4124cSDan Handley 	fvp_pwrc_write_pcoffr(mpidr);
93*3fc4124cSDan Handley }
94*3fc4124cSDan Handley 
95*3fc4124cSDan Handley /*******************************************************************************
96*3fc4124cSDan Handley  * FVP handler called when an affinity instance is about to enter standby.
97*3fc4124cSDan Handley  ******************************************************************************/
98*3fc4124cSDan Handley void fvp_affinst_standby(unsigned int power_state)
99*3fc4124cSDan Handley {
100*3fc4124cSDan Handley 	/*
101*3fc4124cSDan Handley 	 * Enter standby state
102*3fc4124cSDan Handley 	 * dsb is good practice before using wfi to enter low power states
103*3fc4124cSDan Handley 	 */
104*3fc4124cSDan Handley 	dsb();
105*3fc4124cSDan Handley 	wfi();
106*3fc4124cSDan Handley }
107*3fc4124cSDan Handley 
108*3fc4124cSDan Handley /*******************************************************************************
109*3fc4124cSDan Handley  * FVP handler called when an affinity instance is about to be turned on. The
110*3fc4124cSDan Handley  * level and mpidr determine the affinity instance.
111*3fc4124cSDan Handley  ******************************************************************************/
112*3fc4124cSDan Handley int fvp_affinst_on(unsigned long mpidr,
113*3fc4124cSDan Handley 		   unsigned long sec_entrypoint,
114*3fc4124cSDan Handley 		   unsigned int afflvl,
115*3fc4124cSDan Handley 		   unsigned int state)
116*3fc4124cSDan Handley {
117*3fc4124cSDan Handley 	int rc = PSCI_E_SUCCESS;
118*3fc4124cSDan Handley 	unsigned int psysr;
119*3fc4124cSDan Handley 
120*3fc4124cSDan Handley 	/*
121*3fc4124cSDan Handley 	 * It's possible to turn on only affinity level 0 i.e. a cpu
122*3fc4124cSDan Handley 	 * on the FVP. Ignore any other affinity level.
123*3fc4124cSDan Handley 	 */
124*3fc4124cSDan Handley 	if (afflvl != MPIDR_AFFLVL0)
125*3fc4124cSDan Handley 		return rc;
126*3fc4124cSDan Handley 
127*3fc4124cSDan Handley 	/*
128*3fc4124cSDan Handley 	 * Ensure that we do not cancel an inflight power off request
129*3fc4124cSDan Handley 	 * for the target cpu. That would leave it in a zombie wfi.
130*3fc4124cSDan Handley 	 * Wait for it to power off, program the jump address for the
131*3fc4124cSDan Handley 	 * target cpu and then program the power controller to turn
132*3fc4124cSDan Handley 	 * that cpu on
133*3fc4124cSDan Handley 	 */
134*3fc4124cSDan Handley 	do {
135*3fc4124cSDan Handley 		psysr = fvp_pwrc_read_psysr(mpidr);
136*3fc4124cSDan Handley 	} while (psysr & PSYSR_AFF_L0);
137*3fc4124cSDan Handley 
138*3fc4124cSDan Handley 	fvp_program_mailbox(mpidr, sec_entrypoint);
139*3fc4124cSDan Handley 	fvp_pwrc_write_pponr(mpidr);
140*3fc4124cSDan Handley 
141*3fc4124cSDan Handley 	return rc;
142*3fc4124cSDan Handley }
143*3fc4124cSDan Handley 
144*3fc4124cSDan Handley /*******************************************************************************
145*3fc4124cSDan Handley  * FVP handler called when an affinity instance is about to be turned off. The
146*3fc4124cSDan Handley  * level and mpidr determine the affinity instance. The 'state' arg. allows the
147*3fc4124cSDan Handley  * platform to decide whether the cluster is being turned off and take apt
148*3fc4124cSDan Handley  * actions.
149*3fc4124cSDan Handley  *
150*3fc4124cSDan Handley  * CAUTION: There is no guarantee that caches will remain turned on across calls
151*3fc4124cSDan Handley  * to this function as each affinity level is dealt with. So do not write & read
152*3fc4124cSDan Handley  * global variables across calls. It will be wise to do flush a write to the
153*3fc4124cSDan Handley  * global to prevent unpredictable results.
154*3fc4124cSDan Handley  ******************************************************************************/
155*3fc4124cSDan Handley void fvp_affinst_off(unsigned int afflvl,
156*3fc4124cSDan Handley 		    unsigned int state)
157*3fc4124cSDan Handley {
158*3fc4124cSDan Handley 	/* Determine if any platform actions need to be executed */
159*3fc4124cSDan Handley 	if (arm_do_affinst_actions(afflvl, state) == -EAGAIN)
160*3fc4124cSDan Handley 		return;
161*3fc4124cSDan Handley 
162*3fc4124cSDan Handley 	/*
163*3fc4124cSDan Handley 	 * If execution reaches this stage then this affinity level will be
164*3fc4124cSDan Handley 	 * suspended. Perform at least the cpu specific actions followed the
165*3fc4124cSDan Handley 	 * cluster specific operations if applicable.
166*3fc4124cSDan Handley 	 */
167*3fc4124cSDan Handley 	fvp_cpu_pwrdwn_common();
168*3fc4124cSDan Handley 
169*3fc4124cSDan Handley 	if (afflvl != MPIDR_AFFLVL0)
170*3fc4124cSDan Handley 		fvp_cluster_pwrdwn_common();
171*3fc4124cSDan Handley 
172*3fc4124cSDan Handley }
173*3fc4124cSDan Handley 
174*3fc4124cSDan Handley /*******************************************************************************
175*3fc4124cSDan Handley  * FVP handler called when an affinity instance is about to be suspended. The
176*3fc4124cSDan Handley  * level and mpidr determine the affinity instance. The 'state' arg. allows the
177*3fc4124cSDan Handley  * platform to decide whether the cluster is being turned off and take apt
178*3fc4124cSDan Handley  * actions.
179*3fc4124cSDan Handley  *
180*3fc4124cSDan Handley  * CAUTION: There is no guarantee that caches will remain turned on across calls
181*3fc4124cSDan Handley  * to this function as each affinity level is dealt with. So do not write & read
182*3fc4124cSDan Handley  * global variables across calls. It will be wise to do flush a write to the
183*3fc4124cSDan Handley  * global to prevent unpredictable results.
184*3fc4124cSDan Handley  ******************************************************************************/
185*3fc4124cSDan Handley void fvp_affinst_suspend(unsigned long sec_entrypoint,
186*3fc4124cSDan Handley 			unsigned int afflvl,
187*3fc4124cSDan Handley 			unsigned int state)
188*3fc4124cSDan Handley {
189*3fc4124cSDan Handley 	unsigned long mpidr;
190*3fc4124cSDan Handley 
191*3fc4124cSDan Handley 	/* Determine if any platform actions need to be executed. */
192*3fc4124cSDan Handley 	if (arm_do_affinst_actions(afflvl, state) == -EAGAIN)
193*3fc4124cSDan Handley 		return;
194*3fc4124cSDan Handley 
195*3fc4124cSDan Handley 	/* Get the mpidr for this cpu */
196*3fc4124cSDan Handley 	mpidr = read_mpidr_el1();
197*3fc4124cSDan Handley 
198*3fc4124cSDan Handley 	/* Program the jump address for the this cpu */
199*3fc4124cSDan Handley 	fvp_program_mailbox(mpidr, sec_entrypoint);
200*3fc4124cSDan Handley 
201*3fc4124cSDan Handley 	/* Program the power controller to enable wakeup interrupts. */
202*3fc4124cSDan Handley 	fvp_pwrc_set_wen(mpidr);
203*3fc4124cSDan Handley 
204*3fc4124cSDan Handley 	/* Perform the common cpu specific operations */
205*3fc4124cSDan Handley 	fvp_cpu_pwrdwn_common();
206*3fc4124cSDan Handley 
207*3fc4124cSDan Handley 	/* Perform the common cluster specific operations */
208*3fc4124cSDan Handley 	if (afflvl != MPIDR_AFFLVL0)
209*3fc4124cSDan Handley 		fvp_cluster_pwrdwn_common();
210*3fc4124cSDan Handley }
211*3fc4124cSDan Handley 
212*3fc4124cSDan Handley /*******************************************************************************
213*3fc4124cSDan Handley  * FVP handler called when an affinity instance has just been powered on after
214*3fc4124cSDan Handley  * being turned off earlier. The level and mpidr determine the affinity
215*3fc4124cSDan Handley  * instance. The 'state' arg. allows the platform to decide whether the cluster
216*3fc4124cSDan Handley  * was turned off prior to wakeup and do what's necessary to setup it up
217*3fc4124cSDan Handley  * correctly.
218*3fc4124cSDan Handley  ******************************************************************************/
219*3fc4124cSDan Handley void fvp_affinst_on_finish(unsigned int afflvl,
220*3fc4124cSDan Handley 			  unsigned int state)
221*3fc4124cSDan Handley {
222*3fc4124cSDan Handley 	unsigned long mpidr;
223*3fc4124cSDan Handley 
224*3fc4124cSDan Handley 	/* Determine if any platform actions need to be executed. */
225*3fc4124cSDan Handley 	if (arm_do_affinst_actions(afflvl, state) == -EAGAIN)
226*3fc4124cSDan Handley 		return;
227*3fc4124cSDan Handley 
228*3fc4124cSDan Handley 	/* Get the mpidr for this cpu */
229*3fc4124cSDan Handley 	mpidr = read_mpidr_el1();
230*3fc4124cSDan Handley 
231*3fc4124cSDan Handley 	/* Perform the common cluster specific operations */
232*3fc4124cSDan Handley 	if (afflvl != MPIDR_AFFLVL0) {
233*3fc4124cSDan Handley 		/*
234*3fc4124cSDan Handley 		 * This CPU might have woken up whilst the cluster was
235*3fc4124cSDan Handley 		 * attempting to power down. In this case the FVP power
236*3fc4124cSDan Handley 		 * controller will have a pending cluster power off request
237*3fc4124cSDan Handley 		 * which needs to be cleared by writing to the PPONR register.
238*3fc4124cSDan Handley 		 * This prevents the power controller from interpreting a
239*3fc4124cSDan Handley 		 * subsequent entry of this cpu into a simple wfi as a power
240*3fc4124cSDan Handley 		 * down request.
241*3fc4124cSDan Handley 		 */
242*3fc4124cSDan Handley 		fvp_pwrc_write_pponr(mpidr);
243*3fc4124cSDan Handley 
244*3fc4124cSDan Handley 		/* Enable coherency if this cluster was off */
245*3fc4124cSDan Handley 		fvp_cci_enable();
246*3fc4124cSDan Handley 	}
247*3fc4124cSDan Handley 
248*3fc4124cSDan Handley 	/*
249*3fc4124cSDan Handley 	 * Clear PWKUPR.WEN bit to ensure interrupts do not interfere
250*3fc4124cSDan Handley 	 * with a cpu power down unless the bit is set again
251*3fc4124cSDan Handley 	 */
252*3fc4124cSDan Handley 	fvp_pwrc_clr_wen(mpidr);
253*3fc4124cSDan Handley 
254*3fc4124cSDan Handley 	/* Zero the jump address in the mailbox for this cpu */
255*3fc4124cSDan Handley 	fvp_program_mailbox(mpidr, 0);
256*3fc4124cSDan Handley 
257*3fc4124cSDan Handley 	/* Enable the gic cpu interface */
258*3fc4124cSDan Handley 	arm_gic_cpuif_setup();
259*3fc4124cSDan Handley 
260*3fc4124cSDan Handley 	/* TODO: This setup is needed only after a cold boot */
261*3fc4124cSDan Handley 	arm_gic_pcpu_distif_setup();
262*3fc4124cSDan Handley }
263*3fc4124cSDan Handley 
264*3fc4124cSDan Handley /*******************************************************************************
265*3fc4124cSDan Handley  * FVP handler called when an affinity instance has just been powered on after
266*3fc4124cSDan Handley  * having been suspended earlier. The level and mpidr determine the affinity
267*3fc4124cSDan Handley  * instance.
268*3fc4124cSDan Handley  * TODO: At the moment we reuse the on finisher and reinitialize the secure
269*3fc4124cSDan Handley  * context. Need to implement a separate suspend finisher.
270*3fc4124cSDan Handley  ******************************************************************************/
271*3fc4124cSDan Handley void fvp_affinst_suspend_finish(unsigned int afflvl,
272*3fc4124cSDan Handley 			       unsigned int state)
273*3fc4124cSDan Handley {
274*3fc4124cSDan Handley 	fvp_affinst_on_finish(afflvl, state);
275*3fc4124cSDan Handley }
276*3fc4124cSDan Handley 
277*3fc4124cSDan Handley /*******************************************************************************
278*3fc4124cSDan Handley  * FVP handlers to shutdown/reboot the system
279*3fc4124cSDan Handley  ******************************************************************************/
280*3fc4124cSDan Handley static void __dead2 fvp_system_off(void)
281*3fc4124cSDan Handley {
282*3fc4124cSDan Handley 	/* Write the System Configuration Control Register */
283*3fc4124cSDan Handley 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
284*3fc4124cSDan Handley 		V2M_CFGCTRL_START |
285*3fc4124cSDan Handley 		V2M_CFGCTRL_RW |
286*3fc4124cSDan Handley 		V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN));
287*3fc4124cSDan Handley 	wfi();
288*3fc4124cSDan Handley 	ERROR("FVP System Off: operation not handled.\n");
289*3fc4124cSDan Handley 	panic();
290*3fc4124cSDan Handley }
291*3fc4124cSDan Handley 
292*3fc4124cSDan Handley static void __dead2 fvp_system_reset(void)
293*3fc4124cSDan Handley {
294*3fc4124cSDan Handley 	/* Write the System Configuration Control Register */
295*3fc4124cSDan Handley 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
296*3fc4124cSDan Handley 		V2M_CFGCTRL_START |
297*3fc4124cSDan Handley 		V2M_CFGCTRL_RW |
298*3fc4124cSDan Handley 		V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT));
299*3fc4124cSDan Handley 	wfi();
300*3fc4124cSDan Handley 	ERROR("FVP System Reset: operation not handled.\n");
301*3fc4124cSDan Handley 	panic();
302*3fc4124cSDan Handley }
303*3fc4124cSDan Handley 
304*3fc4124cSDan Handley /*******************************************************************************
305*3fc4124cSDan Handley  * Export the platform handlers to enable psci to invoke them
306*3fc4124cSDan Handley  ******************************************************************************/
307*3fc4124cSDan Handley static const plat_pm_ops_t fvp_plat_pm_ops = {
308*3fc4124cSDan Handley 	.affinst_standby = fvp_affinst_standby,
309*3fc4124cSDan Handley 	.affinst_on = fvp_affinst_on,
310*3fc4124cSDan Handley 	.affinst_off = fvp_affinst_off,
311*3fc4124cSDan Handley 	.affinst_suspend = fvp_affinst_suspend,
312*3fc4124cSDan Handley 	.affinst_on_finish = fvp_affinst_on_finish,
313*3fc4124cSDan Handley 	.affinst_suspend_finish = fvp_affinst_suspend_finish,
314*3fc4124cSDan Handley 	.system_off = fvp_system_off,
315*3fc4124cSDan Handley 	.system_reset = fvp_system_reset,
316*3fc4124cSDan Handley 	.validate_power_state = arm_validate_power_state
317*3fc4124cSDan Handley };
318*3fc4124cSDan Handley 
319*3fc4124cSDan Handley /*******************************************************************************
320*3fc4124cSDan Handley  * Export the platform specific power ops & initialize the fvp power controller
321*3fc4124cSDan Handley  ******************************************************************************/
322*3fc4124cSDan Handley int platform_setup_pm(const plat_pm_ops_t **plat_ops)
323*3fc4124cSDan Handley {
324*3fc4124cSDan Handley 	*plat_ops = &fvp_plat_pm_ops;
325*3fc4124cSDan Handley 	return 0;
326*3fc4124cSDan Handley }
327