xref: /aosp_15_r20/external/crosvm/devices/src/pci/pm.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #![cfg_attr(windows, allow(dead_code))]
6 
7 use zerocopy::AsBytes;
8 use zerocopy::FromBytes;
9 use zerocopy::FromZeroes;
10 
11 use crate::pci::pci_configuration::PciCapConfig;
12 use crate::pci::pci_configuration::PciCapConfigWriteResult;
13 use crate::pci::pci_configuration::PciCapMapping;
14 use crate::pci::PciCapability;
15 use crate::pci::PciCapabilityID;
16 
17 const PM_CAP_CONTROL_STATE_OFFSET: usize = 1;
18 pub const PM_CAP_LENGTH: usize = 8;
19 const PM_CAP_PME_SUPPORT_D0: u16 = 0x0800;
20 const PM_CAP_PME_SUPPORT_D3_HOT: u16 = 0x4000;
21 const PM_CAP_PME_SUPPORT_D3_COLD: u16 = 0x8000;
22 const PM_CAP_VERSION: u16 = 0x2;
23 const PM_PME_STATUS: u16 = 0x8000;
24 const PM_PME_ENABLE: u16 = 0x100;
25 const PM_NO_SOFT_RESET: u16 = 0x8;
26 const PM_POWER_STATE_MASK: u16 = 0x3;
27 const PM_POWER_STATE_D0: u16 = 0;
28 const PM_POWER_STATE_D3: u16 = 0x3;
29 
30 #[derive(Eq, PartialEq)]
31 pub enum PciDevicePower {
32     D0 = 0,
33     D3 = 3,
34     Unsupported = 0xFF,
35 }
36 
37 #[repr(C)]
38 #[derive(Clone, Copy, AsBytes, FromZeroes, FromBytes)]
39 pub struct PciPmCap {
40     _cap_vndr: u8,
41     _cap_next: u8,
42     pm_cap: u16,
43     pm_control_status: u16,
44     padding: u16,
45 }
46 
47 impl PciCapability for PciPmCap {
bytes(&self) -> &[u8]48     fn bytes(&self) -> &[u8] {
49         self.as_bytes()
50     }
51 
id(&self) -> PciCapabilityID52     fn id(&self) -> PciCapabilityID {
53         PciCapabilityID::PowerManagement
54     }
55 
writable_bits(&self) -> Vec<u32>56     fn writable_bits(&self) -> Vec<u32> {
57         vec![0u32, 0x8103]
58     }
59 }
60 
61 impl PciPmCap {
new() -> Self62     pub fn new() -> Self {
63         PciPmCap {
64             _cap_vndr: 0,
65             _cap_next: 0,
66             pm_cap: Self::default_cap(),
67             pm_control_status: 0,
68             padding: 0,
69         }
70     }
default_cap() -> u1671     pub fn default_cap() -> u16 {
72         let mut cap = PM_CAP_VERSION;
73         // We use ACPI GPEs for PMEs, which are x86_64 only. We should
74         // implement support for native PCIe PMEs to support non-ACPI platforms.
75         if cfg!(target_arch = "x86_64") {
76             cap |= PM_CAP_PME_SUPPORT_D0 | PM_CAP_PME_SUPPORT_D3_HOT | PM_CAP_PME_SUPPORT_D3_COLD
77         }
78         cap
79     }
80 }
81 
82 pub struct PmConfig {
83     power_control_status: u16,
84     cap_mapping: Option<PciCapMapping>,
85 }
86 
87 impl PmConfig {
new(no_soft_reset: bool) -> Self88     pub fn new(no_soft_reset: bool) -> Self {
89         PmConfig {
90             power_control_status: if no_soft_reset { PM_NO_SOFT_RESET } else { 0 },
91             cap_mapping: None,
92         }
93     }
94 
read(&self, data: &mut u32)95     pub fn read(&self, data: &mut u32) {
96         *data = self.power_control_status as u32;
97     }
98 
write(&mut self, offset: u64, data: &[u8])99     pub fn write(&mut self, offset: u64, data: &[u8]) {
100         if offset > 1 {
101             return;
102         }
103 
104         if offset == 0 {
105             self.power_control_status &= !PM_POWER_STATE_MASK;
106             self.power_control_status |= data[0] as u16 & PM_POWER_STATE_MASK;
107         }
108 
109         let write_data = if offset == 0 && (data.len() == 2 || data.len() == 4) {
110             Some((data[1] as u16) << 8)
111         } else if offset == 1 && data.len() == 1 {
112             Some((data[0] as u16) << 8)
113         } else {
114             None
115         };
116 
117         if let Some(write_data) = write_data {
118             if write_data & PM_PME_STATUS != 0 {
119                 // clear PME_STATUS
120                 self.power_control_status &= !PM_PME_STATUS;
121             }
122 
123             if write_data & PM_PME_ENABLE != 0 {
124                 self.power_control_status |= PM_PME_ENABLE;
125             } else {
126                 self.power_control_status &= !PM_PME_ENABLE;
127             }
128         }
129     }
130 
131     /// If device is in D3 and PME is enabled, set PME status, then device could
132     /// inject a pme interrupt into guest
should_trigger_pme(&mut self) -> bool133     pub fn should_trigger_pme(&mut self) -> bool {
134         if self.power_control_status & PM_POWER_STATE_MASK == PM_POWER_STATE_D3
135             && self.power_control_status & PM_PME_ENABLE != 0
136         {
137             self.power_control_status |= PM_PME_STATUS;
138             if let Some(cap_mapping) = &mut self.cap_mapping {
139                 cap_mapping.set_reg(
140                     PM_CAP_CONTROL_STATE_OFFSET,
141                     self.power_control_status as u32,
142                     0xffff,
143                 );
144             }
145             return true;
146         }
147 
148         false
149     }
150 
151     /// Get device power status
get_power_status(&self) -> PciDevicePower152     pub fn get_power_status(&self) -> PciDevicePower {
153         match self.power_control_status & PM_POWER_STATE_MASK {
154             PM_POWER_STATE_D0 => PciDevicePower::D0,
155             PM_POWER_STATE_D3 => PciDevicePower::D3,
156             _ => PciDevicePower::Unsupported,
157         }
158     }
159 }
160 
161 pub struct PmStatusChange {
162     pub from: PciDevicePower,
163     pub to: PciDevicePower,
164 }
165 
166 impl PciCapConfigWriteResult for PmStatusChange {}
167 
168 const PM_CONFIG_READ_MASK: [u32; 2] = [0, 0xffff];
169 
170 impl PciCapConfig for PmConfig {
read_mask(&self) -> &'static [u32]171     fn read_mask(&self) -> &'static [u32] {
172         &PM_CONFIG_READ_MASK
173     }
174 
read_reg(&self, reg_idx: usize) -> u32175     fn read_reg(&self, reg_idx: usize) -> u32 {
176         let mut data = 0;
177         if reg_idx == PM_CAP_CONTROL_STATE_OFFSET {
178             self.read(&mut data);
179         }
180         data
181     }
182 
write_reg( &mut self, reg_idx: usize, offset: u64, data: &[u8], ) -> Option<Box<dyn PciCapConfigWriteResult>>183     fn write_reg(
184         &mut self,
185         reg_idx: usize,
186         offset: u64,
187         data: &[u8],
188     ) -> Option<Box<dyn PciCapConfigWriteResult>> {
189         if reg_idx == PM_CAP_CONTROL_STATE_OFFSET {
190             let from = self.get_power_status();
191             self.write(offset, data);
192             let to = self.get_power_status();
193             if from != to {
194                 return Some(Box::new(PmStatusChange { from, to }));
195             }
196         }
197         None
198     }
199 
set_cap_mapping(&mut self, mapping: PciCapMapping)200     fn set_cap_mapping(&mut self, mapping: PciCapMapping) {
201         self.cap_mapping = Some(mapping);
202     }
203 }
204