#![cfg_attr(windows, allow(dead_code))]
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
use crate::pci::pci_configuration::PciCapConfig;
use crate::pci::pci_configuration::PciCapConfigWriteResult;
use crate::pci::pci_configuration::PciCapMapping;
use crate::pci::PciCapability;
use crate::pci::PciCapabilityID;
const PM_CAP_CONTROL_STATE_OFFSET: usize = 1;
pub const PM_CAP_LENGTH: usize = 8;
const PM_CAP_PME_SUPPORT_D0: u16 = 0x0800;
const PM_CAP_PME_SUPPORT_D3_HOT: u16 = 0x4000;
const PM_CAP_PME_SUPPORT_D3_COLD: u16 = 0x8000;
const PM_CAP_VERSION: u16 = 0x2;
const PM_PME_STATUS: u16 = 0x8000;
const PM_PME_ENABLE: u16 = 0x100;
const PM_NO_SOFT_RESET: u16 = 0x8;
const PM_POWER_STATE_MASK: u16 = 0x3;
const PM_POWER_STATE_D0: u16 = 0;
const PM_POWER_STATE_D3: u16 = 0x3;
#[derive(Eq, PartialEq)]
pub enum PciDevicePower {
D0 = 0,
D3 = 3,
Unsupported = 0xFF,
}
#[repr(C)]
#[derive(Clone, Copy, AsBytes, FromZeroes, FromBytes)]
pub struct PciPmCap {
_cap_vndr: u8,
_cap_next: u8,
pm_cap: u16,
pm_control_status: u16,
padding: u16,
}
impl PciCapability for PciPmCap {
fn bytes(&self) -> &[u8] {
self.as_bytes()
}
fn id(&self) -> PciCapabilityID {
PciCapabilityID::PowerManagement
}
fn writable_bits(&self) -> Vec<u32> {
vec![0u32, 0x8103]
}
}
impl PciPmCap {
pub fn new() -> Self {
PciPmCap {
_cap_vndr: 0,
_cap_next: 0,
pm_cap: Self::default_cap(),
pm_control_status: 0,
padding: 0,
}
}
pub fn default_cap() -> u16 {
let mut cap = PM_CAP_VERSION;
if cfg!(target_arch = "x86_64") {
cap |= PM_CAP_PME_SUPPORT_D0 | PM_CAP_PME_SUPPORT_D3_HOT | PM_CAP_PME_SUPPORT_D3_COLD
}
cap
}
}
pub struct PmConfig {
power_control_status: u16,
cap_mapping: Option<PciCapMapping>,
}
impl PmConfig {
pub fn new(no_soft_reset: bool) -> Self {
PmConfig {
power_control_status: if no_soft_reset { PM_NO_SOFT_RESET } else { 0 },
cap_mapping: None,
}
}
pub fn read(&self, data: &mut u32) {
*data = self.power_control_status as u32;
}
pub fn write(&mut self, offset: u64, data: &[u8]) {
if offset > 1 {
return;
}
if offset == 0 {
self.power_control_status &= !PM_POWER_STATE_MASK;
self.power_control_status |= data[0] as u16 & PM_POWER_STATE_MASK;
}
let write_data = if offset == 0 && (data.len() == 2 || data.len() == 4) {
Some((data[1] as u16) << 8)
} else if offset == 1 && data.len() == 1 {
Some((data[0] as u16) << 8)
} else {
None
};
if let Some(write_data) = write_data {
if write_data & PM_PME_STATUS != 0 {
self.power_control_status &= !PM_PME_STATUS;
}
if write_data & PM_PME_ENABLE != 0 {
self.power_control_status |= PM_PME_ENABLE;
} else {
self.power_control_status &= !PM_PME_ENABLE;
}
}
}
pub fn should_trigger_pme(&mut self) -> bool {
if self.power_control_status & PM_POWER_STATE_MASK == PM_POWER_STATE_D3
&& self.power_control_status & PM_PME_ENABLE != 0
{
self.power_control_status |= PM_PME_STATUS;
if let Some(cap_mapping) = &mut self.cap_mapping {
cap_mapping.set_reg(
PM_CAP_CONTROL_STATE_OFFSET,
self.power_control_status as u32,
0xffff,
);
}
return true;
}
false
}
pub fn get_power_status(&self) -> PciDevicePower {
match self.power_control_status & PM_POWER_STATE_MASK {
PM_POWER_STATE_D0 => PciDevicePower::D0,
PM_POWER_STATE_D3 => PciDevicePower::D3,
_ => PciDevicePower::Unsupported,
}
}
}
pub struct PmStatusChange {
pub from: PciDevicePower,
pub to: PciDevicePower,
}
impl PciCapConfigWriteResult for PmStatusChange {}
const PM_CONFIG_READ_MASK: [u32; 2] = [0, 0xffff];
impl PciCapConfig for PmConfig {
fn read_mask(&self) -> &'static [u32] {
&PM_CONFIG_READ_MASK
}
fn read_reg(&self, reg_idx: usize) -> u32 {
let mut data = 0;
if reg_idx == PM_CAP_CONTROL_STATE_OFFSET {
self.read(&mut data);
}
data
}
fn write_reg(
&mut self,
reg_idx: usize,
offset: u64,
data: &[u8],
) -> Option<Box<dyn PciCapConfigWriteResult>> {
if reg_idx == PM_CAP_CONTROL_STATE_OFFSET {
let from = self.get_power_status();
self.write(offset, data);
let to = self.get_power_status();
if from != to {
return Some(Box::new(PmStatusChange { from, to }));
}
}
None
}
fn set_cap_mapping(&mut self, mapping: PciCapMapping) {
self.cap_mapping = Some(mapping);
}
}