xref: /arm-trusted-firmware/lib/psci/psci_suspend.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu #include <stddef.h>
9*91f16700Schasinglulu 
10*91f16700Schasinglulu #include <arch.h>
11*91f16700Schasinglulu #include <arch_helpers.h>
12*91f16700Schasinglulu #include <common/bl_common.h>
13*91f16700Schasinglulu #include <common/debug.h>
14*91f16700Schasinglulu #include <context.h>
15*91f16700Schasinglulu #include <lib/el3_runtime/context_mgmt.h>
16*91f16700Schasinglulu #include <lib/el3_runtime/cpu_data.h>
17*91f16700Schasinglulu #include <lib/el3_runtime/pubsub_events.h>
18*91f16700Schasinglulu #include <lib/pmf/pmf.h>
19*91f16700Schasinglulu #include <lib/runtime_instr.h>
20*91f16700Schasinglulu #include <plat/common/platform.h>
21*91f16700Schasinglulu 
22*91f16700Schasinglulu #include "psci_private.h"
23*91f16700Schasinglulu 
24*91f16700Schasinglulu /*******************************************************************************
25*91f16700Schasinglulu  * This function does generic and platform specific operations after a wake-up
26*91f16700Schasinglulu  * from standby/retention states at multiple power levels.
27*91f16700Schasinglulu  ******************************************************************************/
28*91f16700Schasinglulu static void psci_suspend_to_standby_finisher(unsigned int cpu_idx,
29*91f16700Schasinglulu 					     unsigned int end_pwrlvl)
30*91f16700Schasinglulu {
31*91f16700Schasinglulu 	unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
32*91f16700Schasinglulu 	psci_power_state_t state_info;
33*91f16700Schasinglulu 
34*91f16700Schasinglulu 	/* Get the parent nodes */
35*91f16700Schasinglulu 	psci_get_parent_pwr_domain_nodes(cpu_idx, end_pwrlvl, parent_nodes);
36*91f16700Schasinglulu 
37*91f16700Schasinglulu 	psci_acquire_pwr_domain_locks(end_pwrlvl, parent_nodes);
38*91f16700Schasinglulu 
39*91f16700Schasinglulu 	/*
40*91f16700Schasinglulu 	 * Find out which retention states this CPU has exited from until the
41*91f16700Schasinglulu 	 * 'end_pwrlvl'. The exit retention state could be deeper than the entry
42*91f16700Schasinglulu 	 * state as a result of state coordination amongst other CPUs post wfi.
43*91f16700Schasinglulu 	 */
44*91f16700Schasinglulu 	psci_get_target_local_pwr_states(end_pwrlvl, &state_info);
45*91f16700Schasinglulu 
46*91f16700Schasinglulu #if ENABLE_PSCI_STAT
47*91f16700Schasinglulu 	plat_psci_stat_accounting_stop(&state_info);
48*91f16700Schasinglulu 	psci_stats_update_pwr_up(end_pwrlvl, &state_info);
49*91f16700Schasinglulu #endif
50*91f16700Schasinglulu 
51*91f16700Schasinglulu 	/*
52*91f16700Schasinglulu 	 * Plat. management: Allow the platform to do operations
53*91f16700Schasinglulu 	 * on waking up from retention.
54*91f16700Schasinglulu 	 */
55*91f16700Schasinglulu 	psci_plat_pm_ops->pwr_domain_suspend_finish(&state_info);
56*91f16700Schasinglulu 
57*91f16700Schasinglulu 	/*
58*91f16700Schasinglulu 	 * Set the requested and target state of this CPU and all the higher
59*91f16700Schasinglulu 	 * power domain levels for this CPU to run.
60*91f16700Schasinglulu 	 */
61*91f16700Schasinglulu 	psci_set_pwr_domains_to_run(end_pwrlvl);
62*91f16700Schasinglulu 
63*91f16700Schasinglulu 	psci_release_pwr_domain_locks(end_pwrlvl, parent_nodes);
64*91f16700Schasinglulu }
65*91f16700Schasinglulu 
66*91f16700Schasinglulu /*******************************************************************************
67*91f16700Schasinglulu  * This function does generic and platform specific suspend to power down
68*91f16700Schasinglulu  * operations.
69*91f16700Schasinglulu  ******************************************************************************/
70*91f16700Schasinglulu static void psci_suspend_to_pwrdown_start(unsigned int end_pwrlvl,
71*91f16700Schasinglulu 					  const entry_point_info_t *ep,
72*91f16700Schasinglulu 					  const psci_power_state_t *state_info)
73*91f16700Schasinglulu {
74*91f16700Schasinglulu 	unsigned int max_off_lvl = psci_find_max_off_lvl(state_info);
75*91f16700Schasinglulu 
76*91f16700Schasinglulu 	PUBLISH_EVENT(psci_suspend_pwrdown_start);
77*91f16700Schasinglulu 
78*91f16700Schasinglulu #if PSCI_OS_INIT_MODE
79*91f16700Schasinglulu #ifdef PLAT_MAX_CPU_SUSPEND_PWR_LVL
80*91f16700Schasinglulu 	end_pwrlvl = PLAT_MAX_CPU_SUSPEND_PWR_LVL;
81*91f16700Schasinglulu #else
82*91f16700Schasinglulu 	end_pwrlvl = PLAT_MAX_PWR_LVL;
83*91f16700Schasinglulu #endif
84*91f16700Schasinglulu #endif
85*91f16700Schasinglulu 
86*91f16700Schasinglulu 	/* Save PSCI target power level for the suspend finisher handler */
87*91f16700Schasinglulu 	psci_set_suspend_pwrlvl(end_pwrlvl);
88*91f16700Schasinglulu 
89*91f16700Schasinglulu 	/*
90*91f16700Schasinglulu 	 * Flush the target power level as it might be accessed on power up with
91*91f16700Schasinglulu 	 * Data cache disabled.
92*91f16700Schasinglulu 	 */
93*91f16700Schasinglulu 	psci_flush_cpu_data(psci_svc_cpu_data.target_pwrlvl);
94*91f16700Schasinglulu 
95*91f16700Schasinglulu 	/*
96*91f16700Schasinglulu 	 * Call the cpu suspend handler registered by the Secure Payload
97*91f16700Schasinglulu 	 * Dispatcher to let it do any book-keeping. If the handler encounters an
98*91f16700Schasinglulu 	 * error, it's expected to assert within
99*91f16700Schasinglulu 	 */
100*91f16700Schasinglulu 	if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_suspend != NULL))
101*91f16700Schasinglulu 		psci_spd_pm->svc_suspend(max_off_lvl);
102*91f16700Schasinglulu 
103*91f16700Schasinglulu #if !HW_ASSISTED_COHERENCY
104*91f16700Schasinglulu 	/*
105*91f16700Schasinglulu 	 * Plat. management: Allow the platform to perform any early
106*91f16700Schasinglulu 	 * actions required to power down the CPU. This might be useful for
107*91f16700Schasinglulu 	 * HW_ASSISTED_COHERENCY = 0 platforms that can safely perform these
108*91f16700Schasinglulu 	 * actions with data caches enabled.
109*91f16700Schasinglulu 	 */
110*91f16700Schasinglulu 	if (psci_plat_pm_ops->pwr_domain_suspend_pwrdown_early != NULL)
111*91f16700Schasinglulu 		psci_plat_pm_ops->pwr_domain_suspend_pwrdown_early(state_info);
112*91f16700Schasinglulu #endif
113*91f16700Schasinglulu 
114*91f16700Schasinglulu 	/*
115*91f16700Schasinglulu 	 * Store the re-entry information for the non-secure world.
116*91f16700Schasinglulu 	 */
117*91f16700Schasinglulu 	cm_init_my_context(ep);
118*91f16700Schasinglulu 
119*91f16700Schasinglulu #if ENABLE_RUNTIME_INSTRUMENTATION
120*91f16700Schasinglulu 
121*91f16700Schasinglulu 	/*
122*91f16700Schasinglulu 	 * Flush cache line so that even if CPU power down happens
123*91f16700Schasinglulu 	 * the timestamp update is reflected in memory.
124*91f16700Schasinglulu 	 */
125*91f16700Schasinglulu 	PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
126*91f16700Schasinglulu 		RT_INSTR_ENTER_CFLUSH,
127*91f16700Schasinglulu 		PMF_CACHE_MAINT);
128*91f16700Schasinglulu #endif
129*91f16700Schasinglulu 
130*91f16700Schasinglulu 	/*
131*91f16700Schasinglulu 	 * Arch. management. Initiate power down sequence.
132*91f16700Schasinglulu 	 * TODO : Introduce a mechanism to query the cache level to flush
133*91f16700Schasinglulu 	 * and the cpu-ops power down to perform from the platform.
134*91f16700Schasinglulu 	 */
135*91f16700Schasinglulu 	psci_pwrdown_cpu(max_off_lvl);
136*91f16700Schasinglulu 
137*91f16700Schasinglulu #if ENABLE_RUNTIME_INSTRUMENTATION
138*91f16700Schasinglulu 	PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
139*91f16700Schasinglulu 		RT_INSTR_EXIT_CFLUSH,
140*91f16700Schasinglulu 		PMF_NO_CACHE_MAINT);
141*91f16700Schasinglulu #endif
142*91f16700Schasinglulu }
143*91f16700Schasinglulu 
144*91f16700Schasinglulu /*******************************************************************************
145*91f16700Schasinglulu  * Top level handler which is called when a cpu wants to suspend its execution.
146*91f16700Schasinglulu  * It is assumed that along with suspending the cpu power domain, power domains
147*91f16700Schasinglulu  * at higher levels until the target power level will be suspended as well. It
148*91f16700Schasinglulu  * coordinates with the platform to negotiate the target state for each of
149*91f16700Schasinglulu  * the power domain level till the target power domain level. It then performs
150*91f16700Schasinglulu  * generic, architectural, platform setup and state management required to
151*91f16700Schasinglulu  * suspend that power domain level and power domain levels below it.
152*91f16700Schasinglulu  * e.g. For a cpu that's to be suspended, it could mean programming the
153*91f16700Schasinglulu  * power controller whereas for a cluster that's to be suspended, it will call
154*91f16700Schasinglulu  * the platform specific code which will disable coherency at the interconnect
155*91f16700Schasinglulu  * level if the cpu is the last in the cluster and also the program the power
156*91f16700Schasinglulu  * controller.
157*91f16700Schasinglulu  *
158*91f16700Schasinglulu  * All the required parameter checks are performed at the beginning and after
159*91f16700Schasinglulu  * the state transition has been done, no further error is expected and it is
160*91f16700Schasinglulu  * not possible to undo any of the actions taken beyond that point.
161*91f16700Schasinglulu  ******************************************************************************/
162*91f16700Schasinglulu int psci_cpu_suspend_start(const entry_point_info_t *ep,
163*91f16700Schasinglulu 			   unsigned int end_pwrlvl,
164*91f16700Schasinglulu 			   psci_power_state_t *state_info,
165*91f16700Schasinglulu 			   unsigned int is_power_down_state)
166*91f16700Schasinglulu {
167*91f16700Schasinglulu 	int rc = PSCI_E_SUCCESS;
168*91f16700Schasinglulu 	bool skip_wfi = false;
169*91f16700Schasinglulu 	unsigned int idx = plat_my_core_pos();
170*91f16700Schasinglulu 	unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
171*91f16700Schasinglulu 
172*91f16700Schasinglulu 	/*
173*91f16700Schasinglulu 	 * This function must only be called on platforms where the
174*91f16700Schasinglulu 	 * CPU_SUSPEND platform hooks have been implemented.
175*91f16700Schasinglulu 	 */
176*91f16700Schasinglulu 	assert((psci_plat_pm_ops->pwr_domain_suspend != NULL) &&
177*91f16700Schasinglulu 	       (psci_plat_pm_ops->pwr_domain_suspend_finish != NULL));
178*91f16700Schasinglulu 
179*91f16700Schasinglulu 	/* Get the parent nodes */
180*91f16700Schasinglulu 	psci_get_parent_pwr_domain_nodes(idx, end_pwrlvl, parent_nodes);
181*91f16700Schasinglulu 
182*91f16700Schasinglulu 	/*
183*91f16700Schasinglulu 	 * This function acquires the lock corresponding to each power
184*91f16700Schasinglulu 	 * level so that by the time all locks are taken, the system topology
185*91f16700Schasinglulu 	 * is snapshot and state management can be done safely.
186*91f16700Schasinglulu 	 */
187*91f16700Schasinglulu 	psci_acquire_pwr_domain_locks(end_pwrlvl, parent_nodes);
188*91f16700Schasinglulu 
189*91f16700Schasinglulu 	/*
190*91f16700Schasinglulu 	 * We check if there are any pending interrupts after the delay
191*91f16700Schasinglulu 	 * introduced by lock contention to increase the chances of early
192*91f16700Schasinglulu 	 * detection that a wake-up interrupt has fired.
193*91f16700Schasinglulu 	 */
194*91f16700Schasinglulu 	if (read_isr_el1() != 0U) {
195*91f16700Schasinglulu 		skip_wfi = true;
196*91f16700Schasinglulu 		goto exit;
197*91f16700Schasinglulu 	}
198*91f16700Schasinglulu 
199*91f16700Schasinglulu #if PSCI_OS_INIT_MODE
200*91f16700Schasinglulu 	if (psci_suspend_mode == OS_INIT) {
201*91f16700Schasinglulu 		/*
202*91f16700Schasinglulu 		 * This function validates the requested state info for
203*91f16700Schasinglulu 		 * OS-initiated mode.
204*91f16700Schasinglulu 		 */
205*91f16700Schasinglulu 		rc = psci_validate_state_coordination(end_pwrlvl, state_info);
206*91f16700Schasinglulu 		if (rc != PSCI_E_SUCCESS) {
207*91f16700Schasinglulu 			skip_wfi = true;
208*91f16700Schasinglulu 			goto exit;
209*91f16700Schasinglulu 		}
210*91f16700Schasinglulu 	} else {
211*91f16700Schasinglulu #endif
212*91f16700Schasinglulu 		/*
213*91f16700Schasinglulu 		 * This function is passed the requested state info and
214*91f16700Schasinglulu 		 * it returns the negotiated state info for each power level upto
215*91f16700Schasinglulu 		 * the end level specified.
216*91f16700Schasinglulu 		 */
217*91f16700Schasinglulu 		psci_do_state_coordination(end_pwrlvl, state_info);
218*91f16700Schasinglulu #if PSCI_OS_INIT_MODE
219*91f16700Schasinglulu 	}
220*91f16700Schasinglulu #endif
221*91f16700Schasinglulu 
222*91f16700Schasinglulu #if PSCI_OS_INIT_MODE
223*91f16700Schasinglulu 	if (psci_plat_pm_ops->pwr_domain_validate_suspend != NULL) {
224*91f16700Schasinglulu 		rc = psci_plat_pm_ops->pwr_domain_validate_suspend(state_info);
225*91f16700Schasinglulu 		if (rc != PSCI_E_SUCCESS) {
226*91f16700Schasinglulu 			skip_wfi = true;
227*91f16700Schasinglulu 			goto exit;
228*91f16700Schasinglulu 		}
229*91f16700Schasinglulu 	}
230*91f16700Schasinglulu #endif
231*91f16700Schasinglulu 
232*91f16700Schasinglulu 	/* Update the target state in the power domain nodes */
233*91f16700Schasinglulu 	psci_set_target_local_pwr_states(end_pwrlvl, state_info);
234*91f16700Schasinglulu 
235*91f16700Schasinglulu #if ENABLE_PSCI_STAT
236*91f16700Schasinglulu 	/* Update the last cpu for each level till end_pwrlvl */
237*91f16700Schasinglulu 	psci_stats_update_pwr_down(end_pwrlvl, state_info);
238*91f16700Schasinglulu #endif
239*91f16700Schasinglulu 
240*91f16700Schasinglulu 	if (is_power_down_state != 0U)
241*91f16700Schasinglulu 		psci_suspend_to_pwrdown_start(end_pwrlvl, ep, state_info);
242*91f16700Schasinglulu 
243*91f16700Schasinglulu 	/*
244*91f16700Schasinglulu 	 * Plat. management: Allow the platform to perform the
245*91f16700Schasinglulu 	 * necessary actions to turn off this cpu e.g. set the
246*91f16700Schasinglulu 	 * platform defined mailbox with the psci entrypoint,
247*91f16700Schasinglulu 	 * program the power controller etc.
248*91f16700Schasinglulu 	 */
249*91f16700Schasinglulu 
250*91f16700Schasinglulu 	psci_plat_pm_ops->pwr_domain_suspend(state_info);
251*91f16700Schasinglulu 
252*91f16700Schasinglulu #if ENABLE_PSCI_STAT
253*91f16700Schasinglulu 	plat_psci_stat_accounting_start(state_info);
254*91f16700Schasinglulu #endif
255*91f16700Schasinglulu 
256*91f16700Schasinglulu exit:
257*91f16700Schasinglulu 	/*
258*91f16700Schasinglulu 	 * Release the locks corresponding to each power level in the
259*91f16700Schasinglulu 	 * reverse order to which they were acquired.
260*91f16700Schasinglulu 	 */
261*91f16700Schasinglulu 	psci_release_pwr_domain_locks(end_pwrlvl, parent_nodes);
262*91f16700Schasinglulu 
263*91f16700Schasinglulu 	if (skip_wfi) {
264*91f16700Schasinglulu 		return rc;
265*91f16700Schasinglulu 	}
266*91f16700Schasinglulu 
267*91f16700Schasinglulu 	if (is_power_down_state != 0U) {
268*91f16700Schasinglulu #if ENABLE_RUNTIME_INSTRUMENTATION
269*91f16700Schasinglulu 
270*91f16700Schasinglulu 		/*
271*91f16700Schasinglulu 		 * Update the timestamp with cache off.  We assume this
272*91f16700Schasinglulu 		 * timestamp can only be read from the current CPU and the
273*91f16700Schasinglulu 		 * timestamp cache line will be flushed before return to
274*91f16700Schasinglulu 		 * normal world on wakeup.
275*91f16700Schasinglulu 		 */
276*91f16700Schasinglulu 		PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
277*91f16700Schasinglulu 		    RT_INSTR_ENTER_HW_LOW_PWR,
278*91f16700Schasinglulu 		    PMF_NO_CACHE_MAINT);
279*91f16700Schasinglulu #endif
280*91f16700Schasinglulu 
281*91f16700Schasinglulu 		/* The function calls below must not return */
282*91f16700Schasinglulu 		if (psci_plat_pm_ops->pwr_domain_pwr_down_wfi != NULL)
283*91f16700Schasinglulu 			psci_plat_pm_ops->pwr_domain_pwr_down_wfi(state_info);
284*91f16700Schasinglulu 		else
285*91f16700Schasinglulu 			psci_power_down_wfi();
286*91f16700Schasinglulu 	}
287*91f16700Schasinglulu 
288*91f16700Schasinglulu #if ENABLE_RUNTIME_INSTRUMENTATION
289*91f16700Schasinglulu 	PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
290*91f16700Schasinglulu 	    RT_INSTR_ENTER_HW_LOW_PWR,
291*91f16700Schasinglulu 	    PMF_NO_CACHE_MAINT);
292*91f16700Schasinglulu #endif
293*91f16700Schasinglulu 
294*91f16700Schasinglulu 	/*
295*91f16700Schasinglulu 	 * We will reach here if only retention/standby states have been
296*91f16700Schasinglulu 	 * requested at multiple power levels. This means that the cpu
297*91f16700Schasinglulu 	 * context will be preserved.
298*91f16700Schasinglulu 	 */
299*91f16700Schasinglulu 	wfi();
300*91f16700Schasinglulu 
301*91f16700Schasinglulu #if ENABLE_RUNTIME_INSTRUMENTATION
302*91f16700Schasinglulu 	PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
303*91f16700Schasinglulu 	    RT_INSTR_EXIT_HW_LOW_PWR,
304*91f16700Schasinglulu 	    PMF_NO_CACHE_MAINT);
305*91f16700Schasinglulu #endif
306*91f16700Schasinglulu 
307*91f16700Schasinglulu 	/*
308*91f16700Schasinglulu 	 * After we wake up from context retaining suspend, call the
309*91f16700Schasinglulu 	 * context retaining suspend finisher.
310*91f16700Schasinglulu 	 */
311*91f16700Schasinglulu 	psci_suspend_to_standby_finisher(idx, end_pwrlvl);
312*91f16700Schasinglulu 
313*91f16700Schasinglulu 	return rc;
314*91f16700Schasinglulu }
315*91f16700Schasinglulu 
316*91f16700Schasinglulu /*******************************************************************************
317*91f16700Schasinglulu  * The following functions finish an earlier suspend request. They
318*91f16700Schasinglulu  * are called by the common finisher routine in psci_common.c. The `state_info`
319*91f16700Schasinglulu  * is the psci_power_state from which this CPU has woken up from.
320*91f16700Schasinglulu  ******************************************************************************/
321*91f16700Schasinglulu void psci_cpu_suspend_finish(unsigned int cpu_idx, const psci_power_state_t *state_info)
322*91f16700Schasinglulu {
323*91f16700Schasinglulu 	unsigned int counter_freq;
324*91f16700Schasinglulu 	unsigned int max_off_lvl;
325*91f16700Schasinglulu 
326*91f16700Schasinglulu 	/* Ensure we have been woken up from a suspended state */
327*91f16700Schasinglulu 	assert((psci_get_aff_info_state() == AFF_STATE_ON) &&
328*91f16700Schasinglulu 		(is_local_state_off(
329*91f16700Schasinglulu 			state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]) != 0));
330*91f16700Schasinglulu 
331*91f16700Schasinglulu 	/*
332*91f16700Schasinglulu 	 * Plat. management: Perform the platform specific actions
333*91f16700Schasinglulu 	 * before we change the state of the cpu e.g. enabling the
334*91f16700Schasinglulu 	 * gic or zeroing the mailbox register. If anything goes
335*91f16700Schasinglulu 	 * wrong then assert as there is no way to recover from this
336*91f16700Schasinglulu 	 * situation.
337*91f16700Schasinglulu 	 */
338*91f16700Schasinglulu 	psci_plat_pm_ops->pwr_domain_suspend_finish(state_info);
339*91f16700Schasinglulu 
340*91f16700Schasinglulu #if !(HW_ASSISTED_COHERENCY || WARMBOOT_ENABLE_DCACHE_EARLY)
341*91f16700Schasinglulu 	/* Arch. management: Enable the data cache, stack memory maintenance. */
342*91f16700Schasinglulu 	psci_do_pwrup_cache_maintenance();
343*91f16700Schasinglulu #endif
344*91f16700Schasinglulu 
345*91f16700Schasinglulu 	/* Re-init the cntfrq_el0 register */
346*91f16700Schasinglulu 	counter_freq = plat_get_syscnt_freq2();
347*91f16700Schasinglulu 	write_cntfrq_el0(counter_freq);
348*91f16700Schasinglulu 
349*91f16700Schasinglulu #if ENABLE_PAUTH
350*91f16700Schasinglulu 	/* Store APIAKey_EL1 key */
351*91f16700Schasinglulu 	set_cpu_data(apiakey[0], read_apiakeylo_el1());
352*91f16700Schasinglulu 	set_cpu_data(apiakey[1], read_apiakeyhi_el1());
353*91f16700Schasinglulu #endif /* ENABLE_PAUTH */
354*91f16700Schasinglulu 
355*91f16700Schasinglulu 	/*
356*91f16700Schasinglulu 	 * Call the cpu suspend finish handler registered by the Secure Payload
357*91f16700Schasinglulu 	 * Dispatcher to let it do any bookeeping. If the handler encounters an
358*91f16700Schasinglulu 	 * error, it's expected to assert within
359*91f16700Schasinglulu 	 */
360*91f16700Schasinglulu 	if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_suspend_finish != NULL)) {
361*91f16700Schasinglulu 		max_off_lvl = psci_find_max_off_lvl(state_info);
362*91f16700Schasinglulu 		assert(max_off_lvl != PSCI_INVALID_PWR_LVL);
363*91f16700Schasinglulu 		psci_spd_pm->svc_suspend_finish(max_off_lvl);
364*91f16700Schasinglulu 	}
365*91f16700Schasinglulu 
366*91f16700Schasinglulu 	/* Invalidate the suspend level for the cpu */
367*91f16700Schasinglulu 	psci_set_suspend_pwrlvl(PSCI_INVALID_PWR_LVL);
368*91f16700Schasinglulu 
369*91f16700Schasinglulu 	PUBLISH_EVENT(psci_suspend_pwrdown_finish);
370*91f16700Schasinglulu }
371