105 lines
2.7 KiB
Rust
105 lines
2.7 KiB
Rust
//! ARM PSCI driver implementation
|
|
|
|
use abi::error::Error;
|
|
use alloc::sync::Arc;
|
|
use device_api::{
|
|
device::{Device, DeviceInitContext},
|
|
CpuBringupDevice, ResetDevice,
|
|
};
|
|
use device_tree::{
|
|
driver::{device_tree_driver, Node, ProbeContext},
|
|
DeviceTreePropertyRead,
|
|
};
|
|
use kernel_arch::{Architecture, ArchitectureImpl};
|
|
|
|
use crate::arch::PLATFORM;
|
|
|
|
enum CallMethod {
|
|
Hvc,
|
|
Smc,
|
|
}
|
|
|
|
/// ARM Power State Coordination Interface driver
|
|
pub struct Psci {
|
|
method: CallMethod,
|
|
cpu_on: u32,
|
|
#[allow(dead_code)]
|
|
cpu_off: u32,
|
|
#[allow(dead_code)]
|
|
cpu_suspend: u32,
|
|
}
|
|
|
|
impl Device for Psci {
|
|
fn display_name(&self) -> &str {
|
|
"ARM PSCI"
|
|
}
|
|
|
|
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
|
PLATFORM.psci.init(self.clone());
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl CpuBringupDevice for Psci {
|
|
unsafe fn start_cpu(&self, id: usize, ip: usize, arg0: usize) -> Result<(), Error> {
|
|
self.call(self.cpu_on as _, id as _, ip as _, arg0 as _);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl ResetDevice for Psci {
|
|
unsafe fn reset(&self) -> ! {
|
|
ArchitectureImpl::set_interrupt_mask(true);
|
|
|
|
self.call(Self::SYSTEM_RESET as _, 0, 0, 0);
|
|
|
|
loop {
|
|
ArchitectureImpl::wait_for_interrupt();
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Psci {
|
|
const SYSTEM_RESET: u32 = 0x84000009;
|
|
|
|
#[inline]
|
|
unsafe fn call(&self, mut x0: u64, x1: u64, x2: u64, x3: u64) -> u64 {
|
|
match self.method {
|
|
CallMethod::Hvc => {
|
|
core::arch::asm!("hvc #0", inlateout("x0") x0, in("x1") x1, in("x2") x2, in("x3") x3)
|
|
}
|
|
CallMethod::Smc => {
|
|
core::arch::asm!("smc #0", inlateout("x0") x0, in("x1") x1, in("x2") x2, in("x3") x3)
|
|
}
|
|
}
|
|
x0
|
|
}
|
|
}
|
|
|
|
device_tree_driver! {
|
|
compatible: ["arm,psci-1.0", "arm,psci"],
|
|
driver: {
|
|
fn probe(&self, node: &Arc<Node>, _context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
|
let method = node.property("method")?;
|
|
let method = match method.as_str()? {
|
|
"hvc" => CallMethod::Hvc,
|
|
"smc" => CallMethod::Smc,
|
|
method => {
|
|
log::warn!("Ignoring PSCI with unknown method: {method:?}");
|
|
return None;
|
|
}
|
|
};
|
|
let cpu_on = node.property("cpu_on")?.read_cell(0, 1)? as u32;
|
|
let cpu_off = node.property("cpu_off")?.read_cell(0, 1)? as u32;
|
|
let cpu_suspend = node.property("cpu_suspend")?.read_cell(0, 1)? as u32;
|
|
|
|
Some(Arc::new(Psci {
|
|
method,
|
|
cpu_on,
|
|
cpu_off,
|
|
cpu_suspend
|
|
}))
|
|
}
|
|
}
|
|
}
|