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