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]);