xref: /rk3399_ARM-atf/plat/arm/board/arm_fpga/fpga_pm.c (revision 7ee4db6e4720ebb45a7c20da06c06091ee95c298)
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 
7*7ee4db6eSOliver Swede #include <assert.h>
8536d906aSOliver Swede 
9*7ee4db6eSOliver Swede #include <lib/psci/psci.h>
10*7ee4db6eSOliver Swede #include <plat/arm/common/plat_arm.h>
11*7ee4db6eSOliver Swede #include <plat/common/platform.h>
12*7ee4db6eSOliver Swede #include <platform_def.h>
13*7ee4db6eSOliver Swede 
14*7ee4db6eSOliver Swede /*
15*7ee4db6eSOliver Swede  * This is a basic PSCI implementation that allows secondary CPUs to be
16*7ee4db6eSOliver Swede  * released from their initial state and continue to the warm boot entrypoint.
17*7ee4db6eSOliver Swede  *
18*7ee4db6eSOliver Swede  * The secondary CPUs are placed in a holding pen and released by calls
19*7ee4db6eSOliver Swede  * to fpga_pwr_domain_on(mpidr), which updates the hold entry for the CPU
20*7ee4db6eSOliver Swede  * specified by the mpidr argument - the (polling) target CPU will then branch
21*7ee4db6eSOliver Swede  * to the BL31 warm boot sequence at the entrypoint address.
22*7ee4db6eSOliver Swede  *
23*7ee4db6eSOliver Swede  * Additionally, the secondary CPUs are kept in a low-power wfe() state
24*7ee4db6eSOliver Swede  * (placed there at the end of each poll) and woken when necessary through
25*7ee4db6eSOliver Swede  * calls to sev() in fpga_pwr_domain_on(mpidr), once the hold state for the
26*7ee4db6eSOliver Swede  * relevant CPU has been updated.
27*7ee4db6eSOliver Swede  *
28*7ee4db6eSOliver Swede  * Hotplug is currently implemented using a wfi-loop, which removes the
29*7ee4db6eSOliver Swede  * dependencies on any power controllers or other mechanism that is specific
30*7ee4db6eSOliver Swede  * to the running system as specified by the FPGA image.
31*7ee4db6eSOliver Swede  */
32*7ee4db6eSOliver Swede 
33*7ee4db6eSOliver Swede uint64_t hold_base[PLATFORM_CORE_COUNT];
34*7ee4db6eSOliver Swede uintptr_t fpga_sec_entrypoint;
35*7ee4db6eSOliver Swede 
36*7ee4db6eSOliver Swede /*
37*7ee4db6eSOliver Swede  * Calls to the CPU specified by the mpidr will set its hold entry to a value
38*7ee4db6eSOliver Swede  * indicating that it should stop polling and branch off to the warm entrypoint.
39*7ee4db6eSOliver Swede  */
40*7ee4db6eSOliver Swede static int fpga_pwr_domain_on(u_register_t mpidr)
41*7ee4db6eSOliver Swede {
42*7ee4db6eSOliver Swede 	unsigned int pos = plat_core_pos_by_mpidr(mpidr);
43*7ee4db6eSOliver Swede 	unsigned long current_mpidr = read_mpidr_el1();
44*7ee4db6eSOliver Swede 
45*7ee4db6eSOliver Swede 	if (mpidr == current_mpidr) {
46*7ee4db6eSOliver Swede 		return PSCI_E_ALREADY_ON;
47*7ee4db6eSOliver Swede 	}
48*7ee4db6eSOliver Swede 	hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO;
49*7ee4db6eSOliver Swede 	flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t));
50*7ee4db6eSOliver Swede 	sev(); /* Wake any CPUs from wfe */
51*7ee4db6eSOliver Swede 
52*7ee4db6eSOliver Swede 	return PSCI_E_SUCCESS;
53*7ee4db6eSOliver Swede }
54*7ee4db6eSOliver Swede 
55*7ee4db6eSOliver Swede static void fpga_pwr_domain_off(const psci_power_state_t *target_state)
56*7ee4db6eSOliver Swede {
57*7ee4db6eSOliver Swede 	while (1) {
58*7ee4db6eSOliver Swede 		wfi();
59*7ee4db6eSOliver Swede 	}
60*7ee4db6eSOliver Swede }
61*7ee4db6eSOliver Swede 
62*7ee4db6eSOliver Swede static void fpga_cpu_standby(plat_local_state_t cpu_state)
63*7ee4db6eSOliver Swede {
64*7ee4db6eSOliver Swede 	/*
65*7ee4db6eSOliver Swede 	 * Enter standby state
66*7ee4db6eSOliver Swede 	 * dsb is good practice before using wfi to enter low power states
67*7ee4db6eSOliver Swede 	 */
68*7ee4db6eSOliver Swede 	u_register_t scr = read_scr_el3();
69*7ee4db6eSOliver Swede 	write_scr_el3(scr|SCR_IRQ_BIT);
70*7ee4db6eSOliver Swede 	dsb();
71*7ee4db6eSOliver Swede 	wfi();
72*7ee4db6eSOliver Swede 	write_scr_el3(scr);
73*7ee4db6eSOliver Swede }
74*7ee4db6eSOliver Swede 
75*7ee4db6eSOliver Swede plat_psci_ops_t plat_fpga_psci_pm_ops = {
76*7ee4db6eSOliver Swede 	.pwr_domain_on = fpga_pwr_domain_on,
77*7ee4db6eSOliver Swede 	.pwr_domain_off = fpga_pwr_domain_off,
78*7ee4db6eSOliver Swede 	.cpu_standby = fpga_cpu_standby
79*7ee4db6eSOliver Swede };
80536d906aSOliver Swede 
81536d906aSOliver Swede int plat_setup_psci_ops(uintptr_t sec_entrypoint,
82536d906aSOliver Swede 			const plat_psci_ops_t **psci_ops)
83536d906aSOliver Swede {
84*7ee4db6eSOliver Swede 	fpga_sec_entrypoint = sec_entrypoint;
85*7ee4db6eSOliver Swede 	flush_dcache_range((uint64_t)&fpga_sec_entrypoint,
86*7ee4db6eSOliver Swede 			   sizeof(fpga_sec_entrypoint));
87*7ee4db6eSOliver Swede 	*psci_ops = &plat_fpga_psci_pm_ops;
88536d906aSOliver Swede 	return 0;
89536d906aSOliver Swede }
90