blob: a306a23d4a6c77b6f7142a7b87b105e345a9f827 [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{
Javier Almansa Sobrino25786a32020-04-02 12:36:16 +010044 int pos = plat_core_pos_by_mpidr(mpidr);
Oliver Swede6e86c5a2019-12-02 13:21:52 +000045 unsigned long current_mpidr = read_mpidr_el1();
46
Javier Almansa Sobrino25786a32020-04-02 12:36:16 +010047 if (pos < 0) {
48 panic();
49 }
50
Oliver Swede6e86c5a2019-12-02 13:21:52 +000051 if (mpidr == current_mpidr) {
52 return PSCI_E_ALREADY_ON;
53 }
54 hold_base[pos] = PLAT_FPGA_HOLD_STATE_GO;
55 flush_dcache_range((uintptr_t)&hold_base[pos], sizeof(uint64_t));
56 sev(); /* Wake any CPUs from wfe */
Oliver Swede8fed2fe2019-11-11 11:11:06 +000057
Oliver Swede6e86c5a2019-12-02 13:21:52 +000058 return PSCI_E_SUCCESS;
59}
60
Oliver Swedeb51da812019-12-03 14:08:21 +000061void fpga_pwr_domain_on_finish(const psci_power_state_t *target_state)
62{
63 fpga_pwr_gic_on_finish();
64}
65
Oliver Swede6e86c5a2019-12-02 13:21:52 +000066static void fpga_pwr_domain_off(const psci_power_state_t *target_state)
67{
Oliver Swedeb51da812019-12-03 14:08:21 +000068 fpga_pwr_gic_off();
69
Oliver Swede6e86c5a2019-12-02 13:21:52 +000070 while (1) {
71 wfi();
72 }
73}
74
75static void fpga_cpu_standby(plat_local_state_t cpu_state)
76{
77 /*
78 * Enter standby state
79 * dsb is good practice before using wfi to enter low power states
80 */
81 u_register_t scr = read_scr_el3();
82 write_scr_el3(scr|SCR_IRQ_BIT);
83 dsb();
84 wfi();
85 write_scr_el3(scr);
86}
87
88plat_psci_ops_t plat_fpga_psci_pm_ops = {
89 .pwr_domain_on = fpga_pwr_domain_on,
Oliver Swedeb51da812019-12-03 14:08:21 +000090 .pwr_domain_on_finish = fpga_pwr_domain_on_finish,
Oliver Swede6e86c5a2019-12-02 13:21:52 +000091 .pwr_domain_off = fpga_pwr_domain_off,
92 .cpu_standby = fpga_cpu_standby
93};
Oliver Swede8fed2fe2019-11-11 11:11:06 +000094
95int plat_setup_psci_ops(uintptr_t sec_entrypoint,
96 const plat_psci_ops_t **psci_ops)
97{
Oliver Swede6e86c5a2019-12-02 13:21:52 +000098 fpga_sec_entrypoint = sec_entrypoint;
99 flush_dcache_range((uint64_t)&fpga_sec_entrypoint,
100 sizeof(fpga_sec_entrypoint));
101 *psci_ops = &plat_fpga_psci_pm_ops;
Oliver Swede8fed2fe2019-11-11 11:11:06 +0000102 return 0;
103}