1#![cfg_attr(windows, allow(dead_code))]
6
7use zerocopy::FromBytes;
8use zerocopy::Immutable;
9use zerocopy::IntoBytes;
10use zerocopy::KnownLayout;
11
12use crate::pci::pci_configuration::PciCapConfig;
13use crate::pci::pci_configuration::PciCapConfigWriteResult;
14use crate::pci::pci_configuration::PciCapMapping;
15use crate::pci::PciCapability;
16use crate::pci::PciCapabilityID;
17
18const PM_CAP_CONTROL_STATE_OFFSET: usize = 1;
19pub const PM_CAP_LENGTH: usize = 8;
20const PM_CAP_PME_SUPPORT_D0: u16 = 0x0800;
21const PM_CAP_PME_SUPPORT_D3_HOT: u16 = 0x4000;
22const PM_CAP_PME_SUPPORT_D3_COLD: u16 = 0x8000;
23const PM_CAP_VERSION: u16 = 0x2;
24const PM_PME_STATUS: u16 = 0x8000;
25const PM_PME_ENABLE: u16 = 0x100;
26const PM_NO_SOFT_RESET: u16 = 0x8;
27const PM_POWER_STATE_MASK: u16 = 0x3;
28const PM_POWER_STATE_D0: u16 = 0;
29const PM_POWER_STATE_D3: u16 = 0x3;
30
31#[derive(Eq, PartialEq)]
32pub enum PciDevicePower {
33 D0 = 0,
34 D3 = 3,
35 Unsupported = 0xFF,
36}
37
38#[repr(C)]
39#[derive(Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout)]
40pub struct PciPmCap {
41 _cap_vndr: u8,
42 _cap_next: u8,
43 pm_cap: u16,
44 pm_control_status: u16,
45 padding: u16,
46}
47
48impl PciCapability for PciPmCap {
49 fn bytes(&self) -> &[u8] {
50 self.as_bytes()
51 }
52
53 fn id(&self) -> PciCapabilityID {
54 PciCapabilityID::PowerManagement
55 }
56
57 fn writable_bits(&self) -> Vec<u32> {
58 vec![0u32, 0x8103]
59 }
60}
61
62impl PciPmCap {
63 pub fn new() -> Self {
64 PciPmCap {
65 _cap_vndr: 0,
66 _cap_next: 0,
67 pm_cap: Self::default_cap(),
68 pm_control_status: 0,
69 padding: 0,
70 }
71 }
72 pub fn default_cap() -> u16 {
73 let mut cap = PM_CAP_VERSION;
74 if cfg!(target_arch = "x86_64") {
77 cap |= PM_CAP_PME_SUPPORT_D0 | PM_CAP_PME_SUPPORT_D3_HOT | PM_CAP_PME_SUPPORT_D3_COLD
78 }
79 cap
80 }
81}
82
83pub struct PmConfig {
84 power_control_status: u16,
85 cap_mapping: Option<PciCapMapping>,
86}
87
88impl PmConfig {
89 pub fn new(no_soft_reset: bool) -> Self {
90 PmConfig {
91 power_control_status: if no_soft_reset { PM_NO_SOFT_RESET } else { 0 },
92 cap_mapping: None,
93 }
94 }
95
96 pub fn read(&self, data: &mut u32) {
97 *data = self.power_control_status as u32;
98 }
99
100 pub fn write(&mut self, offset: u64, data: &[u8]) {
101 if offset > 1 {
102 return;
103 }
104
105 if offset == 0 {
106 self.power_control_status &= !PM_POWER_STATE_MASK;
107 self.power_control_status |= data[0] as u16 & PM_POWER_STATE_MASK;
108 }
109
110 let write_data = if offset == 0 && (data.len() == 2 || data.len() == 4) {
111 Some((data[1] as u16) << 8)
112 } else if offset == 1 && data.len() == 1 {
113 Some((data[0] as u16) << 8)
114 } else {
115 None
116 };
117
118 if let Some(write_data) = write_data {
119 if write_data & PM_PME_STATUS != 0 {
120 self.power_control_status &= !PM_PME_STATUS;
122 }
123
124 if write_data & PM_PME_ENABLE != 0 {
125 self.power_control_status |= PM_PME_ENABLE;
126 } else {
127 self.power_control_status &= !PM_PME_ENABLE;
128 }
129 }
130 }
131
132 pub fn should_trigger_pme(&mut self) -> bool {
135 if self.power_control_status & PM_POWER_STATE_MASK == PM_POWER_STATE_D3
136 && self.power_control_status & PM_PME_ENABLE != 0
137 {
138 self.power_control_status |= PM_PME_STATUS;
139 if let Some(cap_mapping) = &mut self.cap_mapping {
140 cap_mapping.set_reg(
141 PM_CAP_CONTROL_STATE_OFFSET,
142 self.power_control_status as u32,
143 0xffff,
144 );
145 }
146 return true;
147 }
148
149 false
150 }
151
152 pub fn get_power_status(&self) -> PciDevicePower {
154 match self.power_control_status & PM_POWER_STATE_MASK {
155 PM_POWER_STATE_D0 => PciDevicePower::D0,
156 PM_POWER_STATE_D3 => PciDevicePower::D3,
157 _ => PciDevicePower::Unsupported,
158 }
159 }
160}
161
162pub struct PmStatusChange {
163 pub from: PciDevicePower,
164 pub to: PciDevicePower,
165}
166
167impl PciCapConfigWriteResult for PmStatusChange {}
168
169const PM_CONFIG_READ_MASK: [u32; 2] = [0, 0xffff];
170
171impl PciCapConfig for PmConfig {
172 fn read_mask(&self) -> &'static [u32] {
173 &PM_CONFIG_READ_MASK
174 }
175
176 fn read_reg(&self, reg_idx: usize) -> u32 {
177 let mut data = 0;
178 if reg_idx == PM_CAP_CONTROL_STATE_OFFSET {
179 self.read(&mut data);
180 }
181 data
182 }
183
184 fn write_reg(
185 &mut self,
186 reg_idx: usize,
187 offset: u64,
188 data: &[u8],
189 ) -> Option<Box<dyn PciCapConfigWriteResult>> {
190 if reg_idx == PM_CAP_CONTROL_STATE_OFFSET {
191 let from = self.get_power_status();
192 self.write(offset, data);
193 let to = self.get_power_status();
194 if from != to {
195 return Some(Box::new(PmStatusChange { from, to }));
196 }
197 }
198 None
199 }
200
201 fn set_cap_mapping(&mut self, mapping: PciCapMapping) {
202 self.cap_mapping = Some(mapping);
203 }
204}