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