Add FVP Base Power Controller driver

Change-Id: Ie66850ae966366708321573802efc679f2b149c2
Signed-off-by: Tomás González <tomasagustin.gonzalezorlando@arm.com>
diff --git a/Cargo.lock b/Cargo.lock
index 8f49c08..b3983aa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,8 +9,10 @@
  "arm-gic",
  "arm-pl011-uart",
  "arm-sp805",
+ "bitflags",
  "safe-mmio",
  "spin",
+ "zerocopy",
 ]
 
 [[package]]
@@ -101,9 +103,9 @@
 
 [[package]]
 name = "safe-mmio"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bc59a975d8faa0b4475145266e9f9eac01f0b3f393d0e5eecc8b88646ca2d29"
+checksum = "f96d86dc25e8f742cf117e00b58db99e0c3505208babc33f124f0067f6c22024"
 dependencies = [
  "zerocopy",
 ]
@@ -153,18 +155,18 @@
 
 [[package]]
 name = "zerocopy"
-version = "0.8.23"
+version = "0.8.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6"
+checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.8.23"
+version = "0.8.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
+checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Cargo.toml b/Cargo.toml
index 20eb5c9..4132e4b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,3 +20,5 @@
 arm-sp805 = { git = "https://git.trustedfirmware.org/rust-spmc/arm-sp805.git" }
 safe-mmio = "0.2"
 spin = { version = "0.9", default-features = false, features = ["spin_mutex"] }
+bitflags = "2.9"
+zerocopy = { version = "0.8", features = ["derive"] }
diff --git a/src/lib.rs b/src/lib.rs
index 185ab21..bb954de 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,11 +12,13 @@
 pub use arm_pl011_uart;
 pub use arm_sp805;
 pub use safe_mmio::{PhysicalInstance, UniqueMmioPointer};
+pub mod power_controller;
 
 use arm_gic::GICDRegisters;
 use arm_pl011_uart::PL011Registers;
 use arm_sp805::SP805Registers;
 use core::fmt::Debug;
+use power_controller::FvpPowerControllerRegisters;
 use spin::mutex::Mutex;
 
 static PERIPHERALS_TAKEN: Mutex<bool> = Mutex::new(false);
@@ -30,6 +32,7 @@
     pub uart3: PhysicalInstance<PL011Registers>,
     pub watchdog: PhysicalInstance<SP805Registers>,
     pub gicd: PhysicalInstance<GICDRegisters>,
+    pub power_controller: PhysicalInstance<FvpPowerControllerRegisters>,
 }
 
 impl Peripherals {
@@ -57,6 +60,7 @@
             uart2: PhysicalInstance::new(0x1c0b_0000),
             uart3: PhysicalInstance::new(0x1c0c_0000),
             watchdog: PhysicalInstance::new(0x1c0f_0000),
+            power_controller: PhysicalInstance::new(0x1c10_0000),
             gicd: PhysicalInstance::new(0x2f00_0000),
         }
     }
diff --git a/src/power_controller.rs b/src/power_controller.rs
new file mode 100644
index 0000000..4ca07dc
--- /dev/null
+++ b/src/power_controller.rs
@@ -0,0 +1,313 @@
+// SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use bitflags::bitflags;
+use safe_mmio::UniqueMmioPointer;
+use safe_mmio::{field, fields::ReadPureWrite};
+use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
+
+// Register descriptions
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum PowerOnReason {
+    /// Cold power-on.
+    ColdPowerOn,
+    /// System reset pin.
+    SystemResetPin,
+    /// Wake by PPONR.
+    WakeByProcessorOn,
+    /// Wake by GIC WakeRequest signal.
+    WakeByGicSignal,
+}
+
+impl PowerOnReason {
+    const SHIFT: u32 = 24;
+    const MASK: u32 = 0b11;
+    const COLD_PWR_ON: u32 = 0b00;
+    const SYS_RESET_PIN: u32 = 0b01;
+    const BY_PPONR: u32 = 0b10;
+    const BY_GIC_WAKE_REQ_SIGNAL: u32 = 0b11;
+}
+
+impl From<u32> for PowerOnReason {
+    fn from(value: u32) -> Self {
+        let masked_shifted_value = (value >> Self::SHIFT) & Self::MASK;
+
+        match masked_shifted_value {
+            Self::COLD_PWR_ON => PowerOnReason::ColdPowerOn,
+            Self::SYS_RESET_PIN => PowerOnReason::SystemResetPin,
+            Self::BY_PPONR => PowerOnReason::WakeByProcessorOn,
+            Self::BY_GIC_WAKE_REQ_SIGNAL => PowerOnReason::WakeByGicSignal,
+            _ => unreachable!(),
+        }
+    }
+}
+
+bitflags! {
+    /// Power Control Wakeup Register
+    struct WakeupRegister: u32 {
+        /// If set, enables wakeup interrupts (return from SUSPEND) for this cluster.
+        const WEN = 1 << 31;
+    }
+
+    /// Power Control SYS Status Register
+    pub struct SysStatusRegister: u32 {
+        /// A value of 1 indicates that affinity level 2 is active/on. If affinity level 2 is not
+        /// implemented this bit is RAZ.
+        const L2 = 1 << 31;
+        /// A value of 1 indicates that affinity level 1 is active/on. If affinity level 1 is not
+        /// implemented this bit is RAZ.
+        const L1 = 1 << 30;
+        /// A value of 1 indicates that affinity level 0 is active/on.
+        const L0 = 1 << 29;
+        /// A value of 1 indicates wakeup interrupts, return from SUSPEND, enabled for this
+        /// processor. This is an alias of PWKUPR.WEN for this core.
+        const WEN = 1 << 28;
+        /// A value of 1 indicates pending cluster off, the cluster enters low-power mode the next
+        /// time it raises signal STANDBYWFIL2.
+        const PC = 1 << 27;
+        /// A value of 1 indicates pending processor off, the processor enters low-power mode the
+        /// next time it raises signal STANDBYWFI.
+        const PP = 1 << 26;
+    }
+}
+
+/// FVP Power Controller register map
+#[derive(Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
+#[repr(C, align(4))]
+pub struct FvpPowerControllerRegisters {
+    /// 0x00 - Power Control Processor Off Register
+    ppoffr: ReadPureWrite<u32>,
+    /// 0x04 - Power Control Processor On Register
+    pponr: ReadPureWrite<u32>,
+    /// 0x08 - Power Control Cluster Off Register
+    pcoffr: ReadPureWrite<u32>,
+    /// 0x0C - Power Control Wakeup Register
+    pwkupr: ReadPureWrite<u32>,
+    /// 0x10 - Power Control SYS Status Register
+    psysr: ReadPureWrite<u32>,
+}
+
+/// FVP Power Controller implementation
+pub struct FvpPowerController<'a> {
+    regs: UniqueMmioPointer<'a, FvpPowerControllerRegisters>,
+}
+
+impl<'a> FvpPowerController<'a> {
+    const MPIDR_MASK: u32 = 0xff_ffff;
+
+    /// Creates new FVP Power Controller instance.
+    pub fn new(regs: UniqueMmioPointer<'a, FvpPowerControllerRegisters>) -> Self {
+        Self { regs }
+    }
+
+    // Provides the value of the PSYS register as a u32 for internal use only.
+    fn system_status_reg(&mut self, mpidr: u32) -> u32 {
+        field!(self.regs, psysr).write(mpidr & Self::MPIDR_MASK);
+        field!(self.regs, psysr).read()
+    }
+
+    /// Provides information on the powered status of a given core.
+    ///
+    /// This is done by writing the ID for the required core to the PSYS register and then reading
+    /// the value along with the associated status.
+    /// Please see `power_on_reason` for other related information.
+    pub fn system_status(&mut self, mpidr: u32) -> SysStatusRegister {
+        // There are no usage constraints
+        SysStatusRegister::from_bits_truncate(self.system_status_reg(mpidr))
+    }
+
+    /// Brings up the given processor from low-power mode by writing to the PPONR register
+    ///
+    /// Processor must make power-on requests only for other powered-off processors in the system,
+    /// otherwise get a Programming error.
+    pub fn power_on_processor(&mut self, mpidr: u32) {
+        field!(self.regs, pponr).write(mpidr & Self::MPIDR_MASK);
+    }
+
+    /// Processor SUSPEND command (by writing to the PPOFFR register)
+    ///
+    /// when PWKUPR and the GIC are programmed appropriately to provide wakeup events from IRQ and
+    /// FIQ events to that processor.
+    /// Processor must make power-off requests only for itself, otherwise get a Programming error.
+    pub fn power_off_processor(&mut self, mpidr: u32) {
+        field!(self.regs, ppoffr).write(mpidr & Self::MPIDR_MASK);
+    }
+
+    /// Turns the cluster off
+    ///
+    /// Cluster must make power-off requests only for itself, otherwise get a Programming error.
+    pub fn power_off_cluster(&mut self, mpidr: u32) {
+        field!(self.regs, pcoffr).write(mpidr & Self::MPIDR_MASK);
+    }
+
+    /// Configures whether wakeup requests from the GIC are enabled for this cluster
+    pub fn disable_wakeup_requests(&mut self, mpidr: u32) {
+        // There are no usage constraints
+        let wkup_reg = WakeupRegister::empty();
+
+        field!(self.regs, pwkupr).write((mpidr & Self::MPIDR_MASK) | wkup_reg.bits());
+    }
+
+    /// Configures whether wakeup requests from the GIC are enabled for this cluster
+    pub fn enable_wakeup_requests(&mut self, mpidr: u32) {
+        // There are no usage constraints
+        let wkup_reg = WakeupRegister::empty().union(WakeupRegister::WEN);
+
+        field!(self.regs, pwkupr).write((mpidr & Self::MPIDR_MASK) | wkup_reg.bits());
+    }
+
+    /// Provides information on the reason for Power On of the given core.
+    ///
+    /// This is done by writing the ID for the required core to the PSYS register and reading the
+    /// value along with the associated status.
+    /// Please see `system_status` for other related information.
+    pub fn power_on_reason(&mut self, mpidr: u32) -> PowerOnReason {
+        PowerOnReason::from(self.system_status_reg(mpidr))
+    }
+}
+
+// SAFETY: An `&FvpPowerController` only allows operations which read registers, which can safely be
+// done from multiple threads simultaneously.
+unsafe impl Sync for FvpPowerController<'_> {}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use zerocopy::transmute_mut;
+
+    pub struct FakeFvpPowerControllerRegisters {
+        regs: [u32; 5],
+    }
+
+    impl FakeFvpPowerControllerRegisters {
+        pub fn new() -> Self {
+            Self { regs: [0u32; 5] }
+        }
+
+        #[allow(unused)]
+        pub fn reg_write(&mut self, offset: usize, value: u32) {
+            self.regs[offset / 4] = value;
+        }
+
+        pub fn reg_read(&self, offset: usize) -> u32 {
+            self.regs[offset / 4]
+        }
+
+        fn get(&mut self) -> UniqueMmioPointer<FvpPowerControllerRegisters> {
+            UniqueMmioPointer::from(transmute_mut!(&mut self.regs))
+        }
+
+        pub fn fvp_power_controller_for_test(&mut self) -> FvpPowerController {
+            FvpPowerController::new(self.get())
+        }
+    }
+
+    #[test]
+    fn regs_size() {
+        assert_eq!(core::mem::size_of::<FvpPowerControllerRegisters>(), 0x14);
+    }
+
+    #[test]
+    fn sys_status() {
+        let mut regs = FakeFvpPowerControllerRegisters::new();
+        let fake_mpidr = 111234;
+        let mut fvp_power_controller = regs.fvp_power_controller_for_test();
+
+        let sys_status = fvp_power_controller.system_status(fake_mpidr);
+
+        assert!(!sys_status.contains(SysStatusRegister::L2));
+        assert!(!sys_status.contains(SysStatusRegister::L1));
+        assert!(!sys_status.contains(SysStatusRegister::L0));
+        assert!(!sys_status.contains(SysStatusRegister::WEN));
+        assert!(!sys_status.contains(SysStatusRegister::PC));
+        assert!(!sys_status.contains(SysStatusRegister::PP));
+    }
+
+    #[test]
+    fn pwkupr() {
+        let mut regs = FakeFvpPowerControllerRegisters::new();
+        let fake_mpidr = 865032;
+        let wen_flag = 1 << 31;
+        {
+            let mut fvp_power_controller = regs.fvp_power_controller_for_test();
+            fvp_power_controller.enable_wakeup_requests(fake_mpidr);
+
+            assert_eq!(regs.reg_read(0x0C), fake_mpidr | wen_flag);
+        }
+
+        {
+            let mut fvp_power_controller = regs.fvp_power_controller_for_test();
+            fvp_power_controller.disable_wakeup_requests(fake_mpidr);
+
+            assert_eq!(regs.reg_read(0x0C), fake_mpidr);
+        }
+    }
+
+    #[test]
+    fn pponr() {
+        let mut regs = FakeFvpPowerControllerRegisters::new();
+        let fake_mpidr = 865032;
+        let mut fvp_power_controller = regs.fvp_power_controller_for_test();
+        fvp_power_controller.power_off_processor(fake_mpidr);
+        assert_eq!(regs.reg_read(0x00), fake_mpidr);
+    }
+
+    #[test]
+    fn ppoffr() {
+        let mut regs = FakeFvpPowerControllerRegisters::new();
+        let fake_mpidr = 865032;
+        let mut fvp_power_controller = regs.fvp_power_controller_for_test();
+        fvp_power_controller.power_on_processor(fake_mpidr);
+        assert_eq!(regs.reg_read(0x04), fake_mpidr);
+    }
+
+    #[test]
+    fn pcoffr() {
+        let mut regs = FakeFvpPowerControllerRegisters::new();
+        let fake_mpidr = 0b1010_0010_1010_0010_1010_0010;
+        let mut fvp_power_controller = regs.fvp_power_controller_for_test();
+        fvp_power_controller.power_off_cluster(fake_mpidr);
+        assert_eq!(regs.reg_read(0x08), fake_mpidr);
+    }
+
+    #[test]
+    fn power_on_reason() {
+        let mut regs = FakeFvpPowerControllerRegisters::new();
+        let fake_mpidr = 0b1010_0010_1010_0010_1010_0010;
+        let mut fvp_power_controller = regs.fvp_power_controller_for_test();
+        assert_eq!(
+            fvp_power_controller.power_on_reason(fake_mpidr),
+            PowerOnReason::ColdPowerOn
+        );
+    }
+
+    #[test]
+    fn power_on_reason_enum() {
+        // Power on Reason bits: 00 -> Cold power-on.
+        let sysr_cold_pwr_on = 0b1011_1100_1010_0010_1010_0010_1010_0010;
+        // Power on Reason bits: 11 -> Wake by GIC WakeRequest signal.
+        let sysr_gic_wake_req_sig = !sysr_cold_pwr_on;
+        // Power on Reason bits: 01 -> System reset pin.
+        let sysr_system_reset_pin = 0b0110_1001_1010_0110_1111_0000_1010_0010;
+        // Power on Reason bits: 10 -> Wake by PPONR.
+        let sysr_by_pponr = !sysr_system_reset_pin;
+
+        assert_eq!(
+            PowerOnReason::from(sysr_cold_pwr_on),
+            PowerOnReason::ColdPowerOn
+        );
+        assert_eq!(
+            PowerOnReason::from(sysr_gic_wake_req_sig),
+            PowerOnReason::WakeByGicSignal
+        );
+        assert_eq!(
+            PowerOnReason::from(sysr_system_reset_pin),
+            PowerOnReason::SystemResetPin
+        );
+        assert_eq!(
+            PowerOnReason::from(sysr_by_pponr),
+            PowerOnReason::WakeByProcessorOn
+        );
+    }
+}