• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 use crate::pci::PciCapability;
11 use crate::pci::PciCapabilityID;
12 
13 pub const PM_CAP_CONTROL_STATE_OFFSET: usize = 1;
14 pub const PM_CAP_LENGTH: usize = 8;
15 const PM_CAP_PME_SUPPORT_D0: u16 = 0x0800;
16 const PM_CAP_PME_SUPPORT_D3_HOT: u16 = 0x4000;
17 const PM_CAP_PME_SUPPORT_D3_COLD: u16 = 0x8000;
18 const PM_CAP_VERSION: u16 = 0x2;
19 const PM_PME_STATUS: u16 = 0x8000;
20 const PM_PME_ENABLE: u16 = 0x100;
21 const PM_POWER_STATE_MASK: u16 = 0x3;
22 const PM_POWER_STATE_D0: u16 = 0;
23 const PM_POWER_STATE_D3: u16 = 0x3;
24 
25 #[derive(Eq, PartialEq)]
26 pub enum PciDevicePower {
27     D0 = 0,
28     D3 = 3,
29     Unsupported = 0xFF,
30 }
31 
32 #[repr(C)]
33 #[derive(Clone, Copy, AsBytes, FromBytes)]
34 pub struct PciPmCap {
35     _cap_vndr: u8,
36     _cap_next: u8,
37     pm_cap: u16,
38     pm_control_status: u16,
39     padding: u16,
40 }
41 
42 impl PciCapability for PciPmCap {
bytes(&self) -> &[u8]43     fn bytes(&self) -> &[u8] {
44         self.as_bytes()
45     }
46 
id(&self) -> PciCapabilityID47     fn id(&self) -> PciCapabilityID {
48         PciCapabilityID::PowerManagement
49     }
50 
writable_bits(&self) -> Vec<u32>51     fn writable_bits(&self) -> Vec<u32> {
52         vec![0u32, 0x8103]
53     }
54 }
55 
56 impl PciPmCap {
new() -> Self57     pub fn new() -> Self {
58         PciPmCap {
59             _cap_vndr: 0,
60             _cap_next: 0,
61             pm_cap: Self::default_cap(),
62             pm_control_status: 0,
63             padding: 0,
64         }
65     }
default_cap() -> u1666     pub fn default_cap() -> u16 {
67         PM_CAP_VERSION
68             | PM_CAP_PME_SUPPORT_D0
69             | PM_CAP_PME_SUPPORT_D3_HOT
70             | PM_CAP_PME_SUPPORT_D3_COLD
71     }
72 }
73 
74 pub struct PmConfig {
75     power_control_status: u16,
76 }
77 
78 impl PmConfig {
new() -> Self79     pub fn new() -> Self {
80         PmConfig {
81             power_control_status: 0,
82         }
83     }
84 
read(&self, data: &mut u32)85     pub fn read(&self, data: &mut u32) {
86         *data = self.power_control_status as u32;
87     }
88 
write(&mut self, offset: u64, data: &[u8])89     pub fn write(&mut self, offset: u64, data: &[u8]) {
90         if offset > 1 {
91             return;
92         }
93 
94         if offset == 0 {
95             self.power_control_status &= !PM_POWER_STATE_MASK;
96             self.power_control_status |= data[0] as u16 & PM_POWER_STATE_MASK;
97         }
98 
99         let write_data = if offset == 0 && (data.len() == 2 || data.len() == 4) {
100             Some((data[1] as u16) << 8)
101         } else if offset == 1 && data.len() == 1 {
102             Some((data[0] as u16) << 8)
103         } else {
104             None
105         };
106 
107         if let Some(write_data) = write_data {
108             if write_data & PM_PME_STATUS != 0 {
109                 // clear PME_STATUS
110                 self.power_control_status &= !PM_PME_STATUS;
111             }
112 
113             if write_data & PM_PME_ENABLE != 0 {
114                 self.power_control_status |= PM_PME_ENABLE;
115             } else {
116                 self.power_control_status &= !PM_PME_ENABLE;
117             }
118         }
119     }
120 
121     /// If device is in D3 and PME is enabled, set PME status, then device could
122     /// inject a pme interrupt into guest
should_trigger_pme(&mut self) -> bool123     pub fn should_trigger_pme(&mut self) -> bool {
124         if self.power_control_status & PM_POWER_STATE_MASK == PM_POWER_STATE_D3
125             && self.power_control_status & PM_PME_ENABLE != 0
126         {
127             self.power_control_status |= PM_PME_STATUS;
128 
129             return true;
130         }
131 
132         false
133     }
134 
135     /// Get device power status
get_power_status(&self) -> PciDevicePower136     pub fn get_power_status(&self) -> PciDevicePower {
137         match self.power_control_status & PM_POWER_STATE_MASK {
138             PM_POWER_STATE_D0 => PciDevicePower::D0,
139             PM_POWER_STATE_D3 => PciDevicePower::D3,
140             _ => PciDevicePower::Unsupported,
141         }
142     }
143 }
144