use heterob::{bit_numbering::Lsb, endianness::Le, Seq, P11, P3, P4, P9};
use super::ExtendedCapabilityDataError;
pub use super::latency_tolerance_reporting::MaxLatency;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct L1PmSubstates {
pub l1_pm_substates_capabilities: L1PmSubstatesCapabilities,
pub l1_pm_substates_control_1: L1PmSubstatesControl1,
pub l1_pm_substates_control_2: L1PmSubstatesControl2,
}
impl TryFrom<&[u8]> for L1PmSubstates {
type Error = ExtendedCapabilityDataError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
let Seq {
head:
Le((l1_pm_substates_capabilities, l1_pm_substates_control_1, l1_pm_substates_control_2)),
..
} = P3(slice)
.try_into()
.map_err(|_| ExtendedCapabilityDataError {
name: "L1 PM Substates",
size: 12,
})?;
let Lsb((
pci_pm_l1_2_supported,
pci_pm_l1_1_supported,
aspm_l1_2_supported,
aspm_l1_1_supported,
l1_pm_substates_supported,
(),
port_common_mode_restore_time,
port_t_power_on_scale,
(),
port_t_power_on_value,
(),
)) = P11::<u32, 1, 1, 1, 1, 1, 3, 8, 2, 1, 5, 8>(l1_pm_substates_capabilities).into();
let Lsb((
pci_pm_l1_2_enable,
pci_pm_l1_1_enable,
aspm_l1_2_enable,
aspm_l1_1_enable,
(),
common_mode_restore_time,
ltr_l1_2_threshold_value,
(),
ltr_l1_2_threshold_scale,
)) = P9::<u32, 1, 1, 1, 1, 4, 8, 10, 3, 3>(l1_pm_substates_control_1).into();
let Lsb((t_power_on_scale, (), t_power_on_value, ())) =
P4::<u32, 2, 1, 5, 24>(l1_pm_substates_control_2).into();
Ok(Self {
l1_pm_substates_capabilities: L1PmSubstatesCapabilities {
pci_pm_l1_2_supported,
pci_pm_l1_1_supported,
aspm_l1_2_supported,
aspm_l1_1_supported,
l1_pm_substates_supported,
port_common_mode_restore_time,
port_t_power_on: PortTPowerOn {
value: port_t_power_on_value,
scale: From::<u8>::from(port_t_power_on_scale),
},
},
l1_pm_substates_control_1: L1PmSubstatesControl1 {
pci_pm_l1_2_enable,
pci_pm_l1_1_enable,
aspm_l1_2_enable,
aspm_l1_1_enable,
common_mode_restore_time,
ltr_l1_2_threshold: MaxLatency {
value: ltr_l1_2_threshold_value,
scale: ltr_l1_2_threshold_scale,
},
},
l1_pm_substates_control_2: L1PmSubstatesControl2 {
t_power_on: PortTPowerOn {
value: t_power_on_value,
scale: From::<u8>::from(t_power_on_scale),
},
},
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct L1PmSubstatesCapabilities {
pub pci_pm_l1_2_supported: bool,
pub pci_pm_l1_1_supported: bool,
pub aspm_l1_2_supported: bool,
pub aspm_l1_1_supported: bool,
pub l1_pm_substates_supported: bool,
pub port_common_mode_restore_time: u8,
pub port_t_power_on: PortTPowerOn,
}
impl From<L1PmSubstatesCapabilities> for u32 {
fn from(data: L1PmSubstatesCapabilities) -> Self {
let b0 = u8::from(data.pci_pm_l1_2_supported)
| u8::from(data.pci_pm_l1_1_supported) << 1
| u8::from(data.aspm_l1_2_supported) << 2
| u8::from(data.aspm_l1_1_supported) << 3
| u8::from(data.l1_pm_substates_supported) << 4;
let b2 = u8::from(data.port_t_power_on);
u32::from_le_bytes([b0, data.port_common_mode_restore_time, b2, 0x00])
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PortTPowerOn {
pub value: u8,
pub scale: PortTPowerOnScale,
}
impl PortTPowerOn {
pub fn value(&self) -> Option<usize> {
match self.scale {
PortTPowerOnScale::Reserved => None,
_ => Some(self.value as usize * (self.scale.clone() as usize)),
}
}
}
impl From<PortTPowerOn> for u8 {
fn from(data: PortTPowerOn) -> Self {
(data.value << 3) | u8::from(data.scale)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PortTPowerOnScale {
Time2us = 2,
Time10us = 10,
Time100us = 100,
Reserved = 0,
}
impl From<u8> for PortTPowerOnScale {
fn from(byte: u8) -> Self {
match byte {
0b00 => Self::Time2us,
0b01 => Self::Time10us,
0b10 => Self::Time100us,
_ => Self::Reserved,
}
}
}
impl From<PortTPowerOnScale> for u8 {
fn from(data: PortTPowerOnScale) -> Self {
match data {
PortTPowerOnScale::Time2us => 0,
PortTPowerOnScale::Time10us => 1,
PortTPowerOnScale::Time100us => 2,
PortTPowerOnScale::Reserved => 3,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct L1PmSubstatesControl1 {
pub pci_pm_l1_2_enable: bool,
pub pci_pm_l1_1_enable: bool,
pub aspm_l1_2_enable: bool,
pub aspm_l1_1_enable: bool,
pub common_mode_restore_time: u8,
pub ltr_l1_2_threshold: MaxLatency,
}
impl From<L1PmSubstatesControl1> for u32 {
fn from(data: L1PmSubstatesControl1) -> Self {
let b0 = u8::from(data.pci_pm_l1_2_enable)
| u8::from(data.pci_pm_l1_1_enable) << 1
| u8::from(data.aspm_l1_2_enable) << 2
| u8::from(data.aspm_l1_1_enable) << 3;
let [b2, b3] = u16::from(data.ltr_l1_2_threshold).to_le_bytes();
u32::from_le_bytes([b0, data.common_mode_restore_time, b2, b3])
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct L1PmSubstatesControl2 {
pub t_power_on: PortTPowerOn,
}
impl From<L1PmSubstatesControl2> for u32 {
fn from(data: L1PmSubstatesControl2) -> Self {
u32::from_le_bytes([data.t_power_on.into(), 0x00, 0x00, 0x00])
}
}
#[cfg(test)]
mod tests {
use crate::extended_capabilities::ECH_BYTES;
use pretty_assertions::assert_eq;
use super::*;
const DATA: [u8; 16] = [
0x1e, 0x00, 0x81, 0x12, 0x1f, 0xff, 0x28, 0x00, 0x03, 0x00, 0x32, 0x40, 0xb0, 0x00, 0x00,
0x00,
];
const SAMPLE: L1PmSubstates = L1PmSubstates {
l1_pm_substates_capabilities: L1PmSubstatesCapabilities {
pci_pm_l1_2_supported: true,
pci_pm_l1_1_supported: true,
aspm_l1_2_supported: true,
aspm_l1_1_supported: true,
l1_pm_substates_supported: true,
port_common_mode_restore_time: 255,
port_t_power_on: PortTPowerOn {
value: 5,
scale: PortTPowerOnScale::Time2us,
},
},
l1_pm_substates_control_1: L1PmSubstatesControl1 {
pci_pm_l1_2_enable: true,
pci_pm_l1_1_enable: true,
aspm_l1_2_enable: false,
aspm_l1_1_enable: false,
common_mode_restore_time: 0,
ltr_l1_2_threshold: MaxLatency {
value: 50,
scale: 2,
},
},
l1_pm_substates_control_2: L1PmSubstatesControl2 {
t_power_on: PortTPowerOn {
value: 22,
scale: PortTPowerOnScale::Time2us,
},
},
};
#[test]
fn from_bytes_into_struct() {
let result = DATA[ECH_BYTES..].try_into().unwrap();
assert_eq!(SAMPLE, result);
}
#[test]
fn from_capabilities_into_dword() {
assert_eq!(
u32::from_le_bytes([0x1f, 0xff, 0x28, 0x00]),
SAMPLE.l1_pm_substates_capabilities.into(),
"Capabilities"
);
}
#[test]
fn from_control_1_into_dword() {
assert_eq!(
u32::from_le_bytes([0x03, 0x00, 0x32, 0x40]),
SAMPLE.l1_pm_substates_control_1.into(),
"Control 1"
);
}
#[test]
fn from_control_2_into_dword() {
assert_eq!(
u32::from_le_bytes([0xb0, 0x00, 0x00, 0x00]),
SAMPLE.l1_pm_substates_control_2.into(),
"Control 2"
);
}
}