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