xref: /rk3399_ARM-atf/plat/arm/board/arm_fpga/fpga_pm.c (revision 87762bce84204b38e264bb54804a6bdfbc4d2acf)
1536d906aSOliver Swede /*
2536d906aSOliver Swede  * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
3536d906aSOliver Swede  *
4536d906aSOliver Swede  * SPDX-License-Identifier: BSD-3-Clause
5536d906aSOliver Swede  */
6536d906aSOliver Swede 
77ee4db6eSOliver Swede #include <assert.h>
8536d906aSOliver Swede 
97ee4db6eSOliver Swede #include <lib/psci/psci.h>
107ee4db6eSOliver Swede #include <plat/arm/common/plat_arm.h>
117ee4db6eSOliver Swede #include <plat/common/platform.h>
12*87762bceSOliver Swede 
13*87762bceSOliver Swede #include "fpga_private.h"
147ee4db6eSOliver Swede #include <platform_def.h>
157ee4db6eSOliver Swede 
167ee4db6eSOliver Swede /*
177ee4db6eSOliver Swede  * This is a basic PSCI implementation that allows secondary CPUs to be
187ee4db6eSOliver Swede  * released from their initial state and continue to the warm boot entrypoint.
197ee4db6eSOliver Swede  *
207ee4db6eSOliver Swede  * The secondary CPUs are placed in a holding pen and released by calls
217ee4db6eSOliver Swede  * to fpga_pwr_domain_on(mpidr), which updates the hold entry for the CPU
227ee4db6eSOliver Swede  * specified by the mpidr argument - the (polling) target CPU will then branch
237ee4db6eSOliver Swede  * to the BL31 warm boot sequence at the entrypoint address.
247ee4db6eSOliver Swede  *
257ee4db6eSOliver Swede  * Additionally, the secondary CPUs are kept in a low-power wfe() state
267ee4db6eSOliver Swede  * (placed there at the end of each poll) and woken when necessary through
277ee4db6eSOliver Swede  * calls to sev() in fpga_pwr_domain_on(mpidr), once the hold state for the
287ee4db6eSOliver Swede  * relevant CPU has been updated.
297ee4db6eSOliver Swede  *
307ee4db6eSOliver Swede  * Hotplug is currently implemented using a wfi-loop, which removes the
317ee4db6eSOliver Swede  * dependencies on any power controllers or other mechanism that is specific
327ee4db6eSOliver Swede  * to the running system as specified by the FPGA image.
337ee4db6eSOliver Swede  */
347ee4db6eSOliver Swede 
357ee4db6eSOliver Swede uint64_t hold_base[PLATFORM_CORE_COUNT];
367ee4db6eSOliver Swede uintptr_t fpga_sec_entrypoint;
377ee4db6eSOliver Swede 
387ee4db6eSOliver Swede /*
397ee4db6eSOliver Swede  * Calls to the CPU specified by the mpidr will set its hold entry to a value
407ee4db6eSOliver Swede  * indicating that it should stop polling and branch off to the warm entrypoint.
417ee4db6eSOliver Swede  */
427ee4db6eSOliver Swede static int fpga_pwr_domain_on(u_register_t mpidr)
437ee4db6eSOliver Swede {
447ee4db6eSOliver Swede 	unsigned int pos = plat_core_pos_by_mpidr(mpidr);
457ee4db6eSOliver Swede 	unsigned long current_mpidr = read_mpidr_el1();
467ee4db6eSOliver Swede 
477ee4db6eSOliver Swede 	if (mpidr == current_mpidr) {
487ee4db6eSOliver Swede 		return PSCI_E_ALREADY_ON;
497ee4db6eSOliver Swede 	}
507ee4db6eSOliver Swede 	hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO;
517ee4db6eSOliver Swede 	flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t));
527ee4db6eSOliver Swede 	sev(); /* Wake any CPUs from wfe */
537ee4db6eSOliver Swede 
547ee4db6eSOliver Swede 	return PSCI_E_SUCCESS;
557ee4db6eSOliver Swede }
567ee4db6eSOliver Swede 
57*87762bceSOliver Swede void fpga_pwr_domain_on_finish(const psci_power_state_t *target_state)
58*87762bceSOliver Swede {
59*87762bceSOliver Swede 	fpga_pwr_gic_on_finish();
60*87762bceSOliver Swede }
61*87762bceSOliver Swede 
627ee4db6eSOliver Swede static void fpga_pwr_domain_off(const psci_power_state_t *target_state)
637ee4db6eSOliver Swede {
64*87762bceSOliver Swede 	fpga_pwr_gic_off();
65*87762bceSOliver Swede 
667ee4db6eSOliver Swede 	while (1) {
677ee4db6eSOliver Swede 		wfi();
687ee4db6eSOliver Swede 	}
697ee4db6eSOliver Swede }
707ee4db6eSOliver Swede 
717ee4db6eSOliver Swede static void fpga_cpu_standby(plat_local_state_t cpu_state)
727ee4db6eSOliver Swede {
737ee4db6eSOliver Swede 	/*
747ee4db6eSOliver Swede 	 * Enter standby state
757ee4db6eSOliver Swede 	 * dsb is good practice before using wfi to enter low power states
767ee4db6eSOliver Swede 	 */
777ee4db6eSOliver Swede 	u_register_t scr = read_scr_el3();
787ee4db6eSOliver Swede 	write_scr_el3(scr|SCR_IRQ_BIT);
797ee4db6eSOliver Swede 	dsb();
807ee4db6eSOliver Swede 	wfi();
817ee4db6eSOliver Swede 	write_scr_el3(scr);
827ee4db6eSOliver Swede }
837ee4db6eSOliver Swede 
847ee4db6eSOliver Swede plat_psci_ops_t plat_fpga_psci_pm_ops = {
857ee4db6eSOliver Swede 	.pwr_domain_on = fpga_pwr_domain_on,
86*87762bceSOliver Swede 	.pwr_domain_on_finish = fpga_pwr_domain_on_finish,
877ee4db6eSOliver Swede 	.pwr_domain_off = fpga_pwr_domain_off,
887ee4db6eSOliver Swede 	.cpu_standby = fpga_cpu_standby
897ee4db6eSOliver Swede };
90536d906aSOliver Swede 
91536d906aSOliver Swede int plat_setup_psci_ops(uintptr_t sec_entrypoint,
92536d906aSOliver Swede 			const plat_psci_ops_t **psci_ops)
93536d906aSOliver Swede {
947ee4db6eSOliver Swede 	fpga_sec_entrypoint = sec_entrypoint;
957ee4db6eSOliver Swede 	flush_dcache_range((uint64_t)&fpga_sec_entrypoint,
967ee4db6eSOliver Swede 			   sizeof(fpga_sec_entrypoint));
977ee4db6eSOliver Swede 	*psci_ops = &plat_fpga_psci_pm_ops;
98536d906aSOliver Swede 	return 0;
99536d906aSOliver Swede }
100