Skip to content

Commit 1665922

Browse files
author
Nicolas Pitre
committed
drivers: pm_cpu_ops: Add ARM FVP CPU power management driver
Add a new PM CPU ops driver for ARM Fixed Virtual Platform (FVP) that enables bare metal SMP support without ARM Trusted Firmware (ATF). The driver provides CPU power-on and system reset operations by directly interfacing with FVP's power controller (PWRC) and V2M system registers. The implementation includes RVBAR_EL3 configuration to redirect secondary CPU reset vectors to Zephyr's image header, enabling proper SMP initialization without firmware assistance. Signed-off-by: Nicolas Pitre <[email protected]>
1 parent 05b77ec commit 1665922

File tree

5 files changed

+233
-0
lines changed

5 files changed

+233
-0
lines changed

boards/arm/fvp_base_revc_2xaemv8a/board.cmake

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,12 @@ if(CONFIG_BUILD_WITH_TFA)
4343
set(FVP_FLASH_FILE ${TFA_BINARY_DIR}/fvp/release/fip.bin)
4444
endif()
4545

46+
elseif(CONFIG_PM_CPU_OPS_FVP)
47+
# Configure RVBAR_EL3 for bare metal SMP when using FVP PM CPU ops driver
48+
set(ARMFVP_FLAGS ${ARMFVP_FLAGS}
49+
# Set RVBAR_EL3 for secondary CPUs to Zephyr image base address
50+
-C cluster0.cpu1.RVBAR=${CONFIG_KERNEL_VM_BASE}
51+
-C cluster0.cpu2.RVBAR=${CONFIG_KERNEL_VM_BASE}
52+
-C cluster0.cpu3.RVBAR=${CONFIG_KERNEL_VM_BASE}
53+
)
4654
endif()

drivers/pm_cpu_ops/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ zephyr_library_sources_ifdef(CONFIG_PM_CPU_OPS pm_cpu_ops_weak_impl.c)
66

77
zephyr_library_sources_ifdef(CONFIG_PM_CPU_OPS_PSCI pm_cpu_ops_psci.c)
88

9+
zephyr_library_sources_ifdef(CONFIG_PM_CPU_OPS_FVP pm_cpu_ops_fvp.c)
10+
911
zephyr_library_sources_ifdef(CONFIG_PSCI_SHELL psci_shell.c)

drivers/pm_cpu_ops/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ config PM_CPU_OPS_PSCI
3030
0022A ("Power State Coordination Interface System Software on
3131
ARM processors").
3232

33+
config PM_CPU_OPS_FVP
34+
bool "Support for ARM FVP CPU power management"
35+
depends on DT_HAS_ARM_FVP_PWRC_ENABLED && !PM_CPU_OPS_PSCI
36+
default y if SMP
37+
select PM_CPU_OPS_HAS_DRIVER
38+
help
39+
Say Y here if you want Zephyr to support CPU power management
40+
operations on ARM Fixed Virtual Platform (FVP) without PSCI.
41+
This driver directly controls the FVP power controller and
42+
V2M system registers for CPU power-on and system reset operations.
43+
3344
config PSCI_SHELL
3445
bool "Support for PSCI interface shell commands"
3546
depends on SHELL && PM_CPU_OPS_PSCI
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright (c) 2025 BayLibre SAS
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT arm_fvp_pwrc
8+
9+
#define LOG_LEVEL CONFIG_PM_CPU_OPS_LOG_LEVEL
10+
#include <zephyr/logging/log.h>
11+
LOG_MODULE_REGISTER(fvp_pm_cpu_ops);
12+
13+
#include <zephyr/kernel.h>
14+
#include <zephyr/drivers/pm_cpu_ops.h>
15+
#include <zephyr/sys/barrier.h>
16+
#include <zephyr/sys/util.h>
17+
#include <zephyr/devicetree.h>
18+
19+
/* FVP Platform Constants */
20+
#define FVP_PWRC_BASE DT_INST_REG_ADDR(0)
21+
#define FVP_V2M_SYSREGS_BASE DT_REG_ADDR(DT_INST_PHANDLE(0, arm_vexpress_sysreg))
22+
23+
/* FVP Power Controller Register Offsets */
24+
#define PWRC_PPONR_OFF 0x4 /* Power-on Request */
25+
#define PWRC_PSYSR_OFF 0x10 /* System Status Register */
26+
27+
/* PSYSR Register Bits */
28+
#define PSYSR_AFF_L0 BIT(29) /* Affinity Level 0 */
29+
30+
/* V2M System Registers */
31+
#define V2M_SYS_CFGCTRL_OFF 0xa4 /* System Configuration Control Register */
32+
33+
/* V2M Configuration Control Register bits */
34+
#define V2M_CFGCTRL_START BIT(31) /* Start operation */
35+
#define V2M_CFGCTRL_RW BIT(30) /* Read/Write operation */
36+
#define V2M_CFGCTRL_FUNC_MASK GENMASK(27, 20) /* Function field */
37+
#define V2M_CFGCTRL_FUNC(fn) FIELD_PREP(V2M_CFGCTRL_FUNC_MASK, (fn))
38+
39+
/* V2M System Configuration Functions */
40+
#define V2M_FUNC_REBOOT 0x09 /* System reboot */
41+
42+
/*
43+
* Memory mapping strategy:
44+
*
45+
* To conserve memory (especially page tables) in Zephyr, we use temporary
46+
* mappings for hardware register access. Each operation maps the required
47+
* registers, performs the operation, then unmaps them immediately.
48+
*
49+
* CPU power operations are infrequent and not performance-critical. Memory
50+
* conservation is therefore more important than runtime optimizations.
51+
*/
52+
#define FVP_REGISTER_MAP_SIZE 0x1000 /* 4KB pages for register mapping */
53+
54+
static inline void fvp_pwrc_write_pponr(uintptr_t pwrc_vaddr, unsigned long mpidr)
55+
{
56+
sys_write32((uint32_t)mpidr, pwrc_vaddr + PWRC_PPONR_OFF);
57+
LOG_DBG("FVP: PPONR write: MPIDR=0x%lx", mpidr);
58+
}
59+
60+
static inline uint32_t fvp_pwrc_read_psysr(uintptr_t pwrc_vaddr, unsigned long mpidr)
61+
{
62+
/* Write MPIDR to PSYSR to select which CPU to query */
63+
sys_write32((uint32_t)mpidr, pwrc_vaddr + PWRC_PSYSR_OFF);
64+
65+
/* Read the status for the selected CPU */
66+
return sys_read32(pwrc_vaddr + PWRC_PSYSR_OFF);
67+
}
68+
69+
70+
static int fvp_cpu_power_on(unsigned long target_mpidr, uintptr_t entry_point)
71+
{
72+
uint8_t *pwrc_vaddr_ptr;
73+
uintptr_t pwrc_vaddr;
74+
uint32_t psysr;
75+
int timeout = 10000; /* 1 second timeout */
76+
77+
LOG_DBG("FVP: Powering on CPU MPIDR=0x%lx, entry=0x%lx",
78+
target_mpidr, entry_point);
79+
80+
/* Map power controller registers once for the entire operation */
81+
k_mem_map_phys_bare(&pwrc_vaddr_ptr, FVP_PWRC_BASE, FVP_REGISTER_MAP_SIZE,
82+
K_MEM_PERM_RW | K_MEM_CACHE_NONE);
83+
pwrc_vaddr = (uintptr_t)pwrc_vaddr_ptr;
84+
85+
/*
86+
* Wait for any pending power-off to complete.
87+
* The target CPU must be in OFF state before we can power it on.
88+
*/
89+
do {
90+
psysr = fvp_pwrc_read_psysr(pwrc_vaddr, target_mpidr);
91+
if (timeout-- <= 0) {
92+
LOG_ERR("FVP: Timeout waiting for CPU 0x%lx power-off "
93+
"to complete, PSYSR=0x%x", target_mpidr, psysr);
94+
k_mem_unmap_phys_bare(pwrc_vaddr_ptr, FVP_REGISTER_MAP_SIZE);
95+
return -ETIMEDOUT;
96+
}
97+
k_busy_wait(100);
98+
} while ((psysr & PSYSR_AFF_L0) != 0);
99+
100+
LOG_DBG("FVP: CPU 0x%lx is powered off (PSYSR=0x%x)", target_mpidr, psysr);
101+
102+
/* Power on the target CPU via FVP power controller */
103+
fvp_pwrc_write_pponr(pwrc_vaddr, target_mpidr);
104+
105+
/* Unmap power controller registers */
106+
k_mem_unmap_phys_bare(pwrc_vaddr_ptr, FVP_REGISTER_MAP_SIZE);
107+
108+
/* Ensure power-on request completes */
109+
barrier_dsync_fence_full();
110+
111+
/* Send event to wake up the target CPU from WFE loop */
112+
__asm__ volatile("sev" ::: "memory");
113+
114+
LOG_DBG("FVP: Power-on request completed for CPU 0x%lx", target_mpidr);
115+
return 0;
116+
}
117+
118+
int pm_cpu_on(unsigned long mpidr, uintptr_t entry_point)
119+
{
120+
return fvp_cpu_power_on(mpidr, entry_point);
121+
}
122+
123+
int pm_cpu_off(void)
124+
{
125+
/*
126+
* This is incompatible with our register mapping strategy.
127+
* A CPU that shuts itself down might not complete the register
128+
* unmapping before losing power.
129+
*/
130+
return -ENOTSUP;
131+
}
132+
133+
static inline void fvp_v2m_sys_cfgwrite(uint32_t function)
134+
{
135+
uint8_t *v2m_vaddr_ptr;
136+
uintptr_t v2m_vaddr;
137+
uint32_t val = V2M_CFGCTRL_START | V2M_CFGCTRL_RW | V2M_CFGCTRL_FUNC(function);
138+
139+
/* Temporarily map V2M system registers */
140+
k_mem_map_phys_bare(&v2m_vaddr_ptr, FVP_V2M_SYSREGS_BASE, FVP_REGISTER_MAP_SIZE,
141+
K_MEM_PERM_RW | K_MEM_CACHE_NONE);
142+
v2m_vaddr = (uintptr_t)v2m_vaddr_ptr;
143+
144+
sys_write32(val, v2m_vaddr + V2M_SYS_CFGCTRL_OFF);
145+
146+
LOG_DBG("FVP: V2M SYS_CFGCTRL write: 0x%x (func=0x%x)", val, function);
147+
148+
/* Unmap V2M registers */
149+
k_mem_unmap_phys_bare(v2m_vaddr_ptr, FVP_REGISTER_MAP_SIZE);
150+
}
151+
152+
int pm_system_reset(unsigned char reset_type)
153+
{
154+
LOG_DBG("FVP: System reset requested (type=%u)", reset_type);
155+
156+
/*
157+
* FVP supports system reset via the V2M System Configuration Controller.
158+
* Both warm and cold reset use the same mechanism - the V2M reboot function.
159+
*/
160+
fvp_v2m_sys_cfgwrite(V2M_FUNC_REBOOT);
161+
162+
/*
163+
* The reset should happen immediately, but in case it doesn't work,
164+
* we'll wait a bit and return an error.
165+
*/
166+
k_busy_wait(1000000); /* Wait 1 second */
167+
168+
LOG_ERR("FVP: System reset failed - system did not reset");
169+
return -ETIMEDOUT;
170+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright (c) 2025 BayLibre SAS
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
ARM Fixed Virtual Platform (FVP) Power Controller (PWRC)
6+
7+
The FVP PWRC provides CPU power management capabilities on ARM Fixed Virtual
8+
Platform models. This hardware block is part of the Base_PowerController
9+
component and controls CPU power states for bare-metal operation when PSCI
10+
firmware is not available.
11+
12+
The PWRC registers include:
13+
- PPOFFR (0x00): Power off processor request
14+
- PPONR (0x04): Power on processor request
15+
- PCOFFR (0x08): Power off cluster request
16+
- PWKUPR (0x0c): Wakeup request control
17+
- PSYSR (0x10): System power status register
18+
19+
For system reset operations, this controller requires access to the V2M
20+
System Registers (vexpress-sysreg).
21+
22+
Reference:
23+
- Fast Models FVP Reference Guide (ARM DUI 0966)
24+
- ARM Trusted Firmware-A: drivers/arm/fvp/fvp_pwrc.c
25+
26+
compatible: "arm,fvp-pwrc"
27+
28+
include: base.yaml
29+
30+
properties:
31+
reg:
32+
required: true
33+
description: |
34+
Base address and size of the FVP Power Controller register block.
35+
Typically 0x1c100000 with size 0x1000 on FVP Base RevC platforms.
36+
37+
arm,vexpress-sysreg:
38+
type: phandle
39+
description: |
40+
Reference to the ARM Versatile Express System Registers (v2m_sysreg) node.
41+
Required for system reset operations via the SYS_CFGCTRL register.
42+
See ARM Motherboard Express µATX Technical Reference Manual (DUI 0447).

0 commit comments

Comments
 (0)