blob: 4ca07dc717be2a8e66fd0b6552c6ad39cc2b23fb [file] [log] [blame]
Tomás González52cb4a62025-03-19 18:41:00 +00001// SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use bitflags::bitflags;
5use safe_mmio::UniqueMmioPointer;
6use safe_mmio::{field, fields::ReadPureWrite};
7use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
8
9// Register descriptions
10
11#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12pub enum PowerOnReason {
13 /// Cold power-on.
14 ColdPowerOn,
15 /// System reset pin.
16 SystemResetPin,
17 /// Wake by PPONR.
18 WakeByProcessorOn,
19 /// Wake by GIC WakeRequest signal.
20 WakeByGicSignal,
21}
22
23impl PowerOnReason {
24 const SHIFT: u32 = 24;
25 const MASK: u32 = 0b11;
26 const COLD_PWR_ON: u32 = 0b00;
27 const SYS_RESET_PIN: u32 = 0b01;
28 const BY_PPONR: u32 = 0b10;
29 const BY_GIC_WAKE_REQ_SIGNAL: u32 = 0b11;
30}
31
32impl From<u32> for PowerOnReason {
33 fn from(value: u32) -> Self {
34 let masked_shifted_value = (value >> Self::SHIFT) & Self::MASK;
35
36 match masked_shifted_value {
37 Self::COLD_PWR_ON => PowerOnReason::ColdPowerOn,
38 Self::SYS_RESET_PIN => PowerOnReason::SystemResetPin,
39 Self::BY_PPONR => PowerOnReason::WakeByProcessorOn,
40 Self::BY_GIC_WAKE_REQ_SIGNAL => PowerOnReason::WakeByGicSignal,
41 _ => unreachable!(),
42 }
43 }
44}
45
46bitflags! {
47 /// Power Control Wakeup Register
48 struct WakeupRegister: u32 {
49 /// If set, enables wakeup interrupts (return from SUSPEND) for this cluster.
50 const WEN = 1 << 31;
51 }
52
53 /// Power Control SYS Status Register
54 pub struct SysStatusRegister: u32 {
55 /// A value of 1 indicates that affinity level 2 is active/on. If affinity level 2 is not
56 /// implemented this bit is RAZ.
57 const L2 = 1 << 31;
58 /// A value of 1 indicates that affinity level 1 is active/on. If affinity level 1 is not
59 /// implemented this bit is RAZ.
60 const L1 = 1 << 30;
61 /// A value of 1 indicates that affinity level 0 is active/on.
62 const L0 = 1 << 29;
63 /// A value of 1 indicates wakeup interrupts, return from SUSPEND, enabled for this
64 /// processor. This is an alias of PWKUPR.WEN for this core.
65 const WEN = 1 << 28;
66 /// A value of 1 indicates pending cluster off, the cluster enters low-power mode the next
67 /// time it raises signal STANDBYWFIL2.
68 const PC = 1 << 27;
69 /// A value of 1 indicates pending processor off, the processor enters low-power mode the
70 /// next time it raises signal STANDBYWFI.
71 const PP = 1 << 26;
72 }
73}
74
75/// FVP Power Controller register map
76#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
77#[repr(C, align(4))]
78pub struct FvpPowerControllerRegisters {
79 /// 0x00 - Power Control Processor Off Register
80 ppoffr: ReadPureWrite<u32>,
81 /// 0x04 - Power Control Processor On Register
82 pponr: ReadPureWrite<u32>,
83 /// 0x08 - Power Control Cluster Off Register
84 pcoffr: ReadPureWrite<u32>,
85 /// 0x0C - Power Control Wakeup Register
86 pwkupr: ReadPureWrite<u32>,
87 /// 0x10 - Power Control SYS Status Register
88 psysr: ReadPureWrite<u32>,
89}
90
91/// FVP Power Controller implementation
92pub struct FvpPowerController<'a> {
93 regs: UniqueMmioPointer<'a, FvpPowerControllerRegisters>,
94}
95
96impl<'a> FvpPowerController<'a> {
97 const MPIDR_MASK: u32 = 0xff_ffff;
98
99 /// Creates new FVP Power Controller instance.
100 pub fn new(regs: UniqueMmioPointer<'a, FvpPowerControllerRegisters>) -> Self {
101 Self { regs }
102 }
103
104 // Provides the value of the PSYS register as a u32 for internal use only.
105 fn system_status_reg(&mut self, mpidr: u32) -> u32 {
106 field!(self.regs, psysr).write(mpidr & Self::MPIDR_MASK);
107 field!(self.regs, psysr).read()
108 }
109
110 /// Provides information on the powered status of a given core.
111 ///
112 /// This is done by writing the ID for the required core to the PSYS register and then reading
113 /// the value along with the associated status.
114 /// Please see `power_on_reason` for other related information.
115 pub fn system_status(&mut self, mpidr: u32) -> SysStatusRegister {
116 // There are no usage constraints
117 SysStatusRegister::from_bits_truncate(self.system_status_reg(mpidr))
118 }
119
120 /// Brings up the given processor from low-power mode by writing to the PPONR register
121 ///
122 /// Processor must make power-on requests only for other powered-off processors in the system,
123 /// otherwise get a Programming error.
124 pub fn power_on_processor(&mut self, mpidr: u32) {
125 field!(self.regs, pponr).write(mpidr & Self::MPIDR_MASK);
126 }
127
128 /// Processor SUSPEND command (by writing to the PPOFFR register)
129 ///
130 /// when PWKUPR and the GIC are programmed appropriately to provide wakeup events from IRQ and
131 /// FIQ events to that processor.
132 /// Processor must make power-off requests only for itself, otherwise get a Programming error.
133 pub fn power_off_processor(&mut self, mpidr: u32) {
134 field!(self.regs, ppoffr).write(mpidr & Self::MPIDR_MASK);
135 }
136
137 /// Turns the cluster off
138 ///
139 /// Cluster must make power-off requests only for itself, otherwise get a Programming error.
140 pub fn power_off_cluster(&mut self, mpidr: u32) {
141 field!(self.regs, pcoffr).write(mpidr & Self::MPIDR_MASK);
142 }
143
144 /// Configures whether wakeup requests from the GIC are enabled for this cluster
145 pub fn disable_wakeup_requests(&mut self, mpidr: u32) {
146 // There are no usage constraints
147 let wkup_reg = WakeupRegister::empty();
148
149 field!(self.regs, pwkupr).write((mpidr & Self::MPIDR_MASK) | wkup_reg.bits());
150 }
151
152 /// Configures whether wakeup requests from the GIC are enabled for this cluster
153 pub fn enable_wakeup_requests(&mut self, mpidr: u32) {
154 // There are no usage constraints
155 let wkup_reg = WakeupRegister::empty().union(WakeupRegister::WEN);
156
157 field!(self.regs, pwkupr).write((mpidr & Self::MPIDR_MASK) | wkup_reg.bits());
158 }
159
160 /// Provides information on the reason for Power On of the given core.
161 ///
162 /// This is done by writing the ID for the required core to the PSYS register and reading the
163 /// value along with the associated status.
164 /// Please see `system_status` for other related information.
165 pub fn power_on_reason(&mut self, mpidr: u32) -> PowerOnReason {
166 PowerOnReason::from(self.system_status_reg(mpidr))
167 }
168}
169
170// SAFETY: An `&FvpPowerController` only allows operations which read registers, which can safely be
171// done from multiple threads simultaneously.
172unsafe impl Sync for FvpPowerController<'_> {}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use zerocopy::transmute_mut;
178
179 pub struct FakeFvpPowerControllerRegisters {
180 regs: [u32; 5],
181 }
182
183 impl FakeFvpPowerControllerRegisters {
184 pub fn new() -> Self {
185 Self { regs: [0u32; 5] }
186 }
187
188 #[allow(unused)]
189 pub fn reg_write(&mut self, offset: usize, value: u32) {
190 self.regs[offset / 4] = value;
191 }
192
193 pub fn reg_read(&self, offset: usize) -> u32 {
194 self.regs[offset / 4]
195 }
196
197 fn get(&mut self) -> UniqueMmioPointer<FvpPowerControllerRegisters> {
198 UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
199 }
200
201 pub fn fvp_power_controller_for_test(&mut self) -> FvpPowerController {
202 FvpPowerController::new(self.get())
203 }
204 }
205
206 #[test]
207 fn regs_size() {
208 assert_eq!(core::mem::size_of::<FvpPowerControllerRegisters>(), 0x14);
209 }
210
211 #[test]
212 fn sys_status() {
213 let mut regs = FakeFvpPowerControllerRegisters::new();
214 let fake_mpidr = 111234;
215 let mut fvp_power_controller = regs.fvp_power_controller_for_test();
216
217 let sys_status = fvp_power_controller.system_status(fake_mpidr);
218
219 assert!(!sys_status.contains(SysStatusRegister::L2));
220 assert!(!sys_status.contains(SysStatusRegister::L1));
221 assert!(!sys_status.contains(SysStatusRegister::L0));
222 assert!(!sys_status.contains(SysStatusRegister::WEN));
223 assert!(!sys_status.contains(SysStatusRegister::PC));
224 assert!(!sys_status.contains(SysStatusRegister::PP));
225 }
226
227 #[test]
228 fn pwkupr() {
229 let mut regs = FakeFvpPowerControllerRegisters::new();
230 let fake_mpidr = 865032;
231 let wen_flag = 1 << 31;
232 {
233 let mut fvp_power_controller = regs.fvp_power_controller_for_test();
234 fvp_power_controller.enable_wakeup_requests(fake_mpidr);
235
236 assert_eq!(regs.reg_read(0x0C), fake_mpidr | wen_flag);
237 }
238
239 {
240 let mut fvp_power_controller = regs.fvp_power_controller_for_test();
241 fvp_power_controller.disable_wakeup_requests(fake_mpidr);
242
243 assert_eq!(regs.reg_read(0x0C), fake_mpidr);
244 }
245 }
246
247 #[test]
248 fn pponr() {
249 let mut regs = FakeFvpPowerControllerRegisters::new();
250 let fake_mpidr = 865032;
251 let mut fvp_power_controller = regs.fvp_power_controller_for_test();
252 fvp_power_controller.power_off_processor(fake_mpidr);
253 assert_eq!(regs.reg_read(0x00), fake_mpidr);
254 }
255
256 #[test]
257 fn ppoffr() {
258 let mut regs = FakeFvpPowerControllerRegisters::new();
259 let fake_mpidr = 865032;
260 let mut fvp_power_controller = regs.fvp_power_controller_for_test();
261 fvp_power_controller.power_on_processor(fake_mpidr);
262 assert_eq!(regs.reg_read(0x04), fake_mpidr);
263 }
264
265 #[test]
266 fn pcoffr() {
267 let mut regs = FakeFvpPowerControllerRegisters::new();
268 let fake_mpidr = 0b1010_0010_1010_0010_1010_0010;
269 let mut fvp_power_controller = regs.fvp_power_controller_for_test();
270 fvp_power_controller.power_off_cluster(fake_mpidr);
271 assert_eq!(regs.reg_read(0x08), fake_mpidr);
272 }
273
274 #[test]
275 fn power_on_reason() {
276 let mut regs = FakeFvpPowerControllerRegisters::new();
277 let fake_mpidr = 0b1010_0010_1010_0010_1010_0010;
278 let mut fvp_power_controller = regs.fvp_power_controller_for_test();
279 assert_eq!(
280 fvp_power_controller.power_on_reason(fake_mpidr),
281 PowerOnReason::ColdPowerOn
282 );
283 }
284
285 #[test]
286 fn power_on_reason_enum() {
287 // Power on Reason bits: 00 -> Cold power-on.
288 let sysr_cold_pwr_on = 0b1011_1100_1010_0010_1010_0010_1010_0010;
289 // Power on Reason bits: 11 -> Wake by GIC WakeRequest signal.
290 let sysr_gic_wake_req_sig = !sysr_cold_pwr_on;
291 // Power on Reason bits: 01 -> System reset pin.
292 let sysr_system_reset_pin = 0b0110_1001_1010_0110_1111_0000_1010_0010;
293 // Power on Reason bits: 10 -> Wake by PPONR.
294 let sysr_by_pponr = !sysr_system_reset_pin;
295
296 assert_eq!(
297 PowerOnReason::from(sysr_cold_pwr_on),
298 PowerOnReason::ColdPowerOn
299 );
300 assert_eq!(
301 PowerOnReason::from(sysr_gic_wake_req_sig),
302 PowerOnReason::WakeByGicSignal
303 );
304 assert_eq!(
305 PowerOnReason::from(sysr_system_reset_pin),
306 PowerOnReason::SystemResetPin
307 );
308 assert_eq!(
309 PowerOnReason::from(sysr_by_pponr),
310 PowerOnReason::WakeByProcessorOn
311 );
312 }
313}