blob: 4c372179f0b23c65ab8dcd31c176f86ee8904c16 [file] [log] [blame]
Oliver Swede8fed2fe2019-11-11 11:11:06 +00001/*
2 * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
Oliver Swede6e86c5a2019-12-02 13:21:52 +00007#include <assert.h>
8
9#include <lib/psci/psci.h>
10#include <plat/arm/common/plat_arm.h>
Oliver Swede8fed2fe2019-11-11 11:11:06 +000011#include <plat/common/platform.h>
Oliver Swedeb51da812019-12-03 14:08:21 +000012
13#include "fpga_private.h"
Oliver Swede6e86c5a2019-12-02 13:21:52 +000014#include <platform_def.h>
15
16/*
17 * This is a basic PSCI implementation that allows secondary CPUs to be
18 * released from their initial state and continue to the warm boot entrypoint.
19 *
20 * The secondary CPUs are placed in a holding pen and released by calls
21 * to fpga_pwr_domain_on(mpidr), which updates the hold entry for the CPU
22 * specified by the mpidr argument - the (polling) target CPU will then branch
23 * to the BL31 warm boot sequence at the entrypoint address.
24 *
25 * Additionally, the secondary CPUs are kept in a low-power wfe() state
26 * (placed there at the end of each poll) and woken when necessary through
27 * calls to sev() in fpga_pwr_domain_on(mpidr), once the hold state for the
28 * relevant CPU has been updated.
29 *
30 * Hotplug is currently implemented using a wfi-loop, which removes the
31 * dependencies on any power controllers or other mechanism that is specific
32 * to the running system as specified by the FPGA image.
33 */
34
35uint64_t hold_base[PLATFORM_CORE_COUNT];
36uintptr_t fpga_sec_entrypoint;
37
38/*
39 * Calls to the CPU specified by the mpidr will set its hold entry to a value
40 * indicating that it should stop polling and branch off to the warm entrypoint.
41 */
42static int fpga_pwr_domain_on(u_register_t mpidr)
43{
44 unsigned int pos = plat_core_pos_by_mpidr(mpidr);
45 unsigned long current_mpidr = read_mpidr_el1();
46
47 if (mpidr == current_mpidr) {
48 return PSCI_E_ALREADY_ON;
49 }
50 hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO;
51 flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t));
52 sev(); /* Wake any CPUs from wfe */
Oliver Swede8fed2fe2019-11-11 11:11:06 +000053
Oliver Swede6e86c5a2019-12-02 13:21:52 +000054 return PSCI_E_SUCCESS;
55}
56
Oliver Swedeb51da812019-12-03 14:08:21 +000057void fpga_pwr_domain_on_finish(const psci_power_state_t *target_state)
58{
59 fpga_pwr_gic_on_finish();
60}
61
Oliver Swede6e86c5a2019-12-02 13:21:52 +000062static void fpga_pwr_domain_off(const psci_power_state_t *target_state)
63{
Oliver Swedeb51da812019-12-03 14:08:21 +000064 fpga_pwr_gic_off();
65
Oliver Swede6e86c5a2019-12-02 13:21:52 +000066 while (1) {
67 wfi();
68 }
69}
70
71static void fpga_cpu_standby(plat_local_state_t cpu_state)
72{
73 /*
74 * Enter standby state
75 * dsb is good practice before using wfi to enter low power states
76 */
77 u_register_t scr = read_scr_el3();
78 write_scr_el3(scr|SCR_IRQ_BIT);
79 dsb();
80 wfi();
81 write_scr_el3(scr);
82}
83
84plat_psci_ops_t plat_fpga_psci_pm_ops = {
85 .pwr_domain_on = fpga_pwr_domain_on,
Oliver Swedeb51da812019-12-03 14:08:21 +000086 .pwr_domain_on_finish = fpga_pwr_domain_on_finish,
Oliver Swede6e86c5a2019-12-02 13:21:52 +000087 .pwr_domain_off = fpga_pwr_domain_off,
88 .cpu_standby = fpga_cpu_standby
89};
Oliver Swede8fed2fe2019-11-11 11:11:06 +000090
91int plat_setup_psci_ops(uintptr_t sec_entrypoint,
92 const plat_psci_ops_t **psci_ops)
93{
Oliver Swede6e86c5a2019-12-02 13:21:52 +000094 fpga_sec_entrypoint = sec_entrypoint;
95 flush_dcache_range((uint64_t)&fpga_sec_entrypoint,
96 sizeof(fpga_sec_entrypoint));
97 *psci_ops = &plat_fpga_psci_pm_ops;
Oliver Swede8fed2fe2019-11-11 11:11:06 +000098 return 0;
99}