pcics/extended_capabilities/
dynamic_power_allocation.rs

1/*!
2# Dynamic Power Allocation (DPA)
3
4A common approach to managing power consumption is through a negotiation between the device
5driver, operating system, and executing applications. Adding Dynamic Power Allocation for such
6devices is anticipated to be done as an extension of that negotiation, through software mechanisms
7that are outside of the scope of this specification. Some devices do not have a device specific driver
8to manage power efficiently. The DPA Capability provides a mechanism to allocate power
9dynamically for these types of devices.
10
11## Struct diagram
12<pre>
13<a href="struct.DynamicPowerAllocation.html">DynamicPowerAllocation</a>
14├─ <a href="struct.DpaCapability.html">DpaCapability</a>
15│  ├─ <a href="enum.TransitionLatencyUnit.html">TransitionLatencyUnit</a>
16│  └─ <a href="enum.PowerAllocationScale.html">PowerAllocationScale</a>
17├─ <a href="struct.DpaStatus.html">DpaStatus</a>
18├─ <a href="struct.DpaControl.html">DpaControl</a>
19└─ <a href="struct.DpaPowerAllocationArray.html">DpaPowerAllocationArray</a>
20</pre>
21
22## Examples
23```rust
24# use pcics::extended_capabilities::dynamic_power_allocation::*;
25let data = [
26    /* 00h */ 0x0c, 0x00, 0x01, 0x00, // Capability header
27    /* 04h */ 0x03, 0x22, 0x55, 0xAA, // DPA Capability
28    /* 08h */ 0x00, 0x11, 0x22, 0x33, // DPA Latency Indicator
29    /* 0Ch */ 0x03, 0x01, // DPA Status
30              0x03, 0x00, // DPA Control
31    /* 10h */ 0x00, 0x11, 0x22, 0x33, // DPA Power Allocation Array
32];
33
34let result: DynamicPowerAllocation = data.as_slice().try_into().unwrap();
35
36let sample = DynamicPowerAllocation {
37    dpa_capability: DpaCapability {
38        substate_max: 3,
39        transition_latency_unit: TransitionLatencyUnit::Unit100ms,
40        power_allocation_scale: PowerAllocationScale::Mul0_1,
41        transition_latency_value_0: 0x55,
42        transition_latency_value_1: 0xAA,
43    },
44    dpa_latency_indicator: 0x33221100,
45    dpa_status: DpaStatus {
46        substate_status: 3,
47        substate_control_enabled: true,
48    },
49    dpa_control: DpaControl {
50        substate_control: 3,
51    },
52    dpa_power_allocation_array: DpaPowerAllocationArray(&[0x00, 0x11, 0x22, 0x33]),
53};
54
55assert_eq!(sample, result);
56```
57*/
58
59use heterob::{bit_numbering::Lsb, endianness::Le, Seq, P2, P4, P8};
60use snafu::Snafu;
61
62use super::ExtendedCapabilityHeader;
63
64#[derive(Snafu, Debug, Clone, PartialEq, Eq)]
65pub enum DynamicPowerAllocationError {
66    #[snafu(display("capability, latency indicator, status and control fields are unreadable"))]
67    Mandatory,
68    #[snafu(display("number of entries must be equal to the Substate_Max plus one (expected: {expected}, found: {found})"))]
69    DpaAllocationArray { expected: usize, found: usize },
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct DynamicPowerAllocation<'a> {
74    pub dpa_capability: DpaCapability,
75    /// Each bit indicates which Transition Latency Value is associated with
76    /// the corresponding substate
77    pub dpa_latency_indicator: u32,
78    pub dpa_status: DpaStatus,
79    pub dpa_control: DpaControl,
80    pub dpa_power_allocation_array: DpaPowerAllocationArray<'a>,
81}
82
83impl<'a> TryFrom<&'a [u8]> for DynamicPowerAllocation<'a> {
84    type Error = DynamicPowerAllocationError;
85
86    fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
87        // Skip header
88        let slice = slice
89            .get(ExtendedCapabilityHeader::SIZE..)
90            .unwrap_or_default();
91        let Seq {
92            head: Le((dpa_capability, dpa_latency_indicator, dpa_status, dpa_control)),
93            tail,
94        } = P4(slice)
95            .try_into()
96            .map_err(|_| DynamicPowerAllocationError::Mandatory)?;
97        let dpa_capability @ DpaCapability { substate_max, .. } = From::<u32>::from(dpa_capability);
98        let substate_max = substate_max as usize;
99        tail.get(..substate_max + 1)
100            .ok_or(DynamicPowerAllocationError::DpaAllocationArray {
101                expected: substate_max,
102                found: tail.len(),
103            })
104            .map(|slice| Self {
105                dpa_capability,
106                dpa_latency_indicator,
107                dpa_control: From::<u16>::from(dpa_control),
108                dpa_status: From::<u16>::from(dpa_status),
109                dpa_power_allocation_array: DpaPowerAllocationArray(slice),
110            })
111    }
112}
113
114/// DPA Capability
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub struct DpaCapability {
117    /// Indicates the maximum substate number, which is the total number of
118    /// supported substates minus one.
119    pub substate_max: u8,
120    pub transition_latency_unit: TransitionLatencyUnit,
121    pub power_allocation_scale: PowerAllocationScale,
122    /// This value is multiplied by the [Transition Latency Unit](TransitionLatencyUnit)
123    /// to determine the maximum Transition Latency for the substate
124    pub transition_latency_value_0: u8,
125    /// This value is multiplied by the [Transition Latency Unit](TransitionLatencyUnit)
126    /// to determine the maximum Transition Latency for the substate
127    pub transition_latency_value_1: u8,
128}
129
130impl From<u32> for DpaCapability {
131    fn from(dword: u32) -> Self {
132        let Lsb((
133            substate_max,
134            (),
135            transition_latency_unit,
136            (),
137            power_allocation_scale,
138            (),
139            transition_latency_value_0,
140            transition_latency_value_1,
141        )) = P8::<_, 5, 3, 2, 2, 2, 2, 8, 8>(dword).into();
142        Self {
143            substate_max,
144            transition_latency_unit: From::<u8>::from(transition_latency_unit),
145            power_allocation_scale: From::<u8>::from(power_allocation_scale),
146            transition_latency_value_0,
147            transition_latency_value_1,
148        }
149    }
150}
151
152/// A substate’s Transition Latency Value is multiplied by the Transition
153/// Latency Unit to determine the maximum Transition Latency for the substate
154#[derive(Debug, Clone, PartialEq, Eq)]
155pub enum TransitionLatencyUnit {
156    /// 1 ms
157    Unit1ms,
158    /// 10 ms
159    Unit10ms,
160    /// 100 ms
161    Unit100ms,
162    /// Reserved
163    Reserved,
164}
165
166impl From<u8> for TransitionLatencyUnit {
167    fn from(byte: u8) -> Self {
168        match byte {
169            0b00 => Self::Unit1ms,
170            0b01 => Self::Unit10ms,
171            0b10 => Self::Unit100ms,
172            0b11 => Self::Reserved,
173            _ => unreachable!(),
174        }
175    }
176}
177
178/// The encodings provide the scale to determine power allocation per substate in Watts
179#[derive(Debug, Clone, PartialEq, Eq)]
180pub enum PowerAllocationScale {
181    /// 10.0x
182    Mul10,
183    /// 1.0x
184    Mul1_0,
185    /// 0.1x
186    Mul0_1,
187    /// 0.01x
188    Mul0_01,
189}
190
191impl From<u8> for PowerAllocationScale {
192    fn from(byte: u8) -> Self {
193        match byte {
194            0b00 => Self::Mul10,
195            0b01 => Self::Mul1_0,
196            0b10 => Self::Mul0_1,
197            0b11 => Self::Mul0_01,
198            _ => unreachable!(),
199        }
200    }
201}
202
203/// DPA Status
204#[derive(Debug, Clone, PartialEq, Eq)]
205pub struct DpaStatus {
206    /// Indicates current substate for this Function
207    pub substate_status: u8,
208    /// When this field is Set, the Substate Control field determines the
209    /// current substate
210    pub substate_control_enabled: bool,
211}
212
213impl From<u16> for DpaStatus {
214    fn from(word: u16) -> Self {
215        let Lsb((substate_status, (), substate_control_enabled, ())) =
216            P4::<_, 5, 3, 1, 7>(word).into();
217        Self {
218            substate_status,
219            substate_control_enabled,
220        }
221    }
222}
223
224/// DPA Control
225#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct DpaControl {
227    pub substate_control: u8,
228}
229
230impl From<u16> for DpaControl {
231    fn from(word: u16) -> Self {
232        let Lsb((substate_control, ())) = P2::<_, 5, 11>(word).into();
233        Self { substate_control }
234    }
235}
236
237/// DPA Power Allocation Array
238///
239/// Each Substate Power Allocation register indicates the power allocation
240/// value for its associated substate
241#[derive(Debug, Clone, PartialEq, Eq)]
242pub struct DpaPowerAllocationArray<'a>(pub &'a [u8]);