axplat_aarch64_peripherals/
psci.rs

1//! ARM Power State Coordination Interface.
2
3#![allow(dead_code)]
4
5use core::sync::atomic::{AtomicBool, Ordering};
6
7const PSCI_0_2_FN_BASE: u32 = 0x84000000;
8const PSCI_0_2_64BIT: u32 = 0x40000000;
9const PSCI_0_2_FN_CPU_SUSPEND: u32 = PSCI_0_2_FN_BASE + 1;
10const PSCI_0_2_FN_CPU_OFF: u32 = PSCI_0_2_FN_BASE + 2;
11const PSCI_0_2_FN_CPU_ON: u32 = PSCI_0_2_FN_BASE + 3;
12const PSCI_0_2_FN_MIGRATE: u32 = PSCI_0_2_FN_BASE + 5;
13const PSCI_0_2_FN_SYSTEM_OFF: u32 = PSCI_0_2_FN_BASE + 8;
14const PSCI_0_2_FN_SYSTEM_RESET: u32 = PSCI_0_2_FN_BASE + 9;
15const PSCI_0_2_FN64_CPU_SUSPEND: u32 = PSCI_0_2_FN_BASE + PSCI_0_2_64BIT + 1;
16const PSCI_0_2_FN64_CPU_ON: u32 = PSCI_0_2_FN_BASE + PSCI_0_2_64BIT + 3;
17const PSCI_0_2_FN64_MIGRATE: u32 = PSCI_0_2_FN_BASE + PSCI_0_2_64BIT + 5;
18
19static PSCI_METHOD_HVC: AtomicBool = AtomicBool::new(false);
20
21/// PSCI return values, inclusive of all PSCI versions.
22#[derive(PartialEq, Debug)]
23#[repr(i32)]
24enum PsciError {
25    NotSupported = -1,
26    InvalidParams = -2,
27    Denied = -3,
28    AlreadyOn = -4,
29    OnPending = -5,
30    InternalFailure = -6,
31    NotPresent = -7,
32    Disabled = -8,
33    InvalidAddress = -9,
34}
35
36impl From<i32> for PsciError {
37    fn from(code: i32) -> PsciError {
38        use PsciError::*;
39        match code {
40            -1 => NotSupported,
41            -2 => InvalidParams,
42            -3 => Denied,
43            -4 => AlreadyOn,
44            -5 => OnPending,
45            -6 => InternalFailure,
46            -7 => NotPresent,
47            -8 => Disabled,
48            -9 => InvalidAddress,
49            _ => panic!("Unknown PSCI error code: {}", code),
50        }
51    }
52}
53
54/// arm,psci method: smc
55/// when SMCCC_CONDUIT_SMC = 1
56fn arm_smccc_smc(func: u32, arg0: usize, arg1: usize, arg2: usize) -> usize {
57    let mut ret;
58    #[cfg(target_arch = "aarch64")]
59    unsafe {
60        core::arch::asm!(
61            "smc #0",
62            inlateout("x0") func as usize => ret,
63            in("x1") arg0,
64            in("x2") arg1,
65            in("x3") arg2,
66        )
67    }
68    #[cfg(target_arch = "arm")]
69    unsafe {
70        core::arch::asm!(
71            ".arch_extension sec",
72            "smc #0",
73            inlateout("r0") func => ret,
74            in("r1") arg0,
75            in("r2") arg1,
76            in("r3") arg2,
77        )
78    }
79    ret
80}
81
82/// psci "hvc" method call
83fn psci_hvc_call(func: u32, arg0: usize, arg1: usize, arg2: usize) -> usize {
84    let ret;
85    #[cfg(target_arch = "aarch64")]
86    unsafe {
87        core::arch::asm!(
88            "hvc #0",
89            inlateout("x0") func as usize => ret,
90            in("x1") arg0,
91            in("x2") arg1,
92            in("x3") arg2,
93        )
94    }
95    #[cfg(target_arch = "arm")]
96    unsafe {
97        core::arch::asm!(
98            ".arch_extension virt",
99            "hvc #0",
100            inlateout("r0") func => ret,
101            in("r1") arg0,
102            in("r2") arg1,
103            in("r3") arg2,
104        )
105    }
106    ret
107}
108
109fn psci_call(func: u32, arg0: usize, arg1: usize, arg2: usize) -> Result<(), PsciError> {
110    let ret = if PSCI_METHOD_HVC.load(Ordering::Acquire) {
111        psci_hvc_call(func, arg0, arg1, arg2)
112    } else {
113        arm_smccc_smc(func, arg0, arg1, arg2)
114    };
115    if ret == 0 {
116        Ok(())
117    } else {
118        Err(PsciError::from(ret as i32))
119    }
120}
121
122/// Initialize with the given PSCI method.
123///
124/// Method should be either "smc" or "hvc".
125pub fn init(method: &str) {
126    match method {
127        "smc" => PSCI_METHOD_HVC.store(false, Ordering::Release),
128        "hvc" => PSCI_METHOD_HVC.store(true, Ordering::Release),
129        _ => panic!("Unknown PSCI method: {}", method),
130    }
131}
132
133/// Shutdown the whole system, including all CPUs.
134pub fn system_off() -> ! {
135    info!("Shutting down...");
136    psci_call(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0).ok();
137    warn!("It should shutdown!");
138    loop {
139        axcpu::asm::halt();
140    }
141}
142
143/// Power up a core. This call is used to power up cores that either:
144///
145/// * Have not yet been booted into the calling supervisory software.
146/// * Have been previously powered down with a `cpu_off` call.
147///
148/// `target_cpu` contains a copy of the affinity fields of the MPIDR register.
149/// `entry_point` is the physical address of the secondary CPU's entry point.
150/// `arg` will be passed to the `X0` register of the secondary CPU.
151pub fn cpu_on(target_cpu: usize, entry_point: usize, arg: usize) {
152    info!("Starting CPU {:x} ON ...", target_cpu);
153    let res = psci_call(PSCI_0_2_FN64_CPU_ON, target_cpu, entry_point, arg);
154    if let Err(e) = res {
155        error!("failed to boot CPU {:x} ({:?})", target_cpu, e);
156    }
157}
158
159/// Power down the calling core. This call is intended for use in hotplug. A
160/// core that is powered down by `cpu_off` can only be powered up again in
161/// response to a `cpu_on`.
162pub fn cpu_off() {
163    const PSCI_POWER_STATE_TYPE_STANDBY: u32 = 0;
164    const PSCI_POWER_STATE_TYPE_POWER_DOWN: u32 = 1;
165    const PSCI_0_2_POWER_STATE_TYPE_SHIFT: u32 = 16;
166    let state: u32 = PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT;
167    psci_call(PSCI_0_2_FN_CPU_OFF, state as usize, 0, 0).ok();
168}