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