1 /* 2 * PROJECT: ReactOS PCI Bus Driver 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: drivers/bus/pci/power.c 5 * PURPOSE: Bus/Device Power Management 6 * PROGRAMMERS: ReactOS Portable Systems Group 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include <pci.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* GLOBALS ********************************************************************/ 17 18 ULONG PciPowerDelayTable[PowerDeviceD3 * PowerDeviceD3] = 19 { 20 0, // D0 -> D0 21 0, // D1 -> D0 22 200, // D2 -> D0 23 10000, // D3 -> D0 24 25 0, // D0 -> D1 26 0, // D1 -> D1 27 200, // D2 -> D1 28 10000, // D3 -> D1 29 30 200, // D0 -> D2 31 200, // D1 -> D2 32 0, // D2 -> D2 33 10000, // D3 -> D2 34 35 10000, // D0 -> D3 36 10000, // D1 -> D3 37 10000, // D2 -> D3 38 0 // D3 -> D3 39 }; 40 41 /* FUNCTIONS ******************************************************************/ 42 43 NTSTATUS 44 NTAPI 45 PciStallForPowerChange(IN PPCI_PDO_EXTENSION PdoExtension, 46 IN DEVICE_POWER_STATE PowerState, 47 IN ULONG_PTR CapOffset) 48 { 49 ULONG PciState, TimeoutEntry, PmcsrOffset, TryCount; 50 PPCI_VERIFIER_DATA VerifierData; 51 LARGE_INTEGER Interval; 52 PCI_PMCSR Pmcsr; 53 KIRQL Irql; 54 55 /* Make sure the power state is valid, and the device can support it */ 56 ASSERT((PdoExtension->PowerState.CurrentDeviceState >= PowerDeviceD0) && 57 (PdoExtension->PowerState.CurrentDeviceState <= PowerDeviceD3)); 58 ASSERT((PowerState >= PowerDeviceD0) && (PowerState <= PowerDeviceD3)); 59 ASSERT(!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)); 60 61 /* Save the current IRQL */ 62 Irql = KeGetCurrentIrql(); 63 64 /* Pick the expected timeout for this transition */ 65 TimeoutEntry = PciPowerDelayTable[PowerState * PdoExtension->PowerState.CurrentDeviceState]; 66 67 /* PCI power states are one less than NT power states */ 68 PciState = PowerState - 1; 69 70 /* The state status is stored in the PMCSR offset */ 71 PmcsrOffset = CapOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR); 72 73 /* Try changing the power state up to 100 times */ 74 TryCount = 100; 75 while (--TryCount) 76 { 77 /* Check if this state transition will take time */ 78 if (TimeoutEntry > 0) 79 { 80 /* Check if this is happening at high IRQL */ 81 if (Irql >= DISPATCH_LEVEL) 82 { 83 /* Can't wait at high IRQL, stall the processor */ 84 KeStallExecutionProcessor(TimeoutEntry); 85 } 86 else 87 { 88 /* Do a wait for the timeout specified instead */ 89 Interval.QuadPart = -10 * TimeoutEntry; 90 Interval.QuadPart -= KeQueryTimeIncrement() - 1; 91 KeDelayExecutionThread(KernelMode, FALSE, &Interval); 92 } 93 } 94 95 /* Read the PMCSR and see if the state has changed */ 96 PciReadDeviceConfig(PdoExtension, &Pmcsr, PmcsrOffset, sizeof(PCI_PMCSR)); 97 if (Pmcsr.PowerState == PciState) return STATUS_SUCCESS; 98 99 /* Try again, forcing a timeout of 1ms */ 100 TimeoutEntry = 1000; 101 } 102 103 /* Call verifier with this error */ 104 VerifierData = PciVerifierRetrieveFailureData(2); 105 ASSERT(VerifierData); 106 VfFailDeviceNode(PdoExtension->PhysicalDeviceObject, 107 PCI_VERIFIER_DETECTED_VIOLATION, 108 2, // The PMCSR register was not updated within the spec-mandated time. 109 VerifierData->FailureClass, 110 &VerifierData->AssertionControl, 111 VerifierData->DebuggerMessageText, 112 "%DevObj%Ulong", 113 PdoExtension->PhysicalDeviceObject, 114 PciState); 115 116 return STATUS_DEVICE_PROTOCOL_ERROR; 117 } 118 119 NTSTATUS 120 NTAPI 121 PciSetPowerManagedDevicePowerState(IN PPCI_PDO_EXTENSION DeviceExtension, 122 IN DEVICE_POWER_STATE DeviceState, 123 IN BOOLEAN IrpSet) 124 { 125 NTSTATUS Status; 126 PCI_PM_CAPABILITY PmCaps; 127 ULONG CapsOffset; 128 129 /* Assume success */ 130 Status = STATUS_SUCCESS; 131 132 /* Check if this device can support low power states */ 133 if (!(PciCanDisableDecodes(DeviceExtension, NULL, 0, TRUE)) && 134 (DeviceState != PowerDeviceD0)) 135 { 136 /* Simply return success, ignoring this request */ 137 DPRINT1("Cannot disable decodes on this device, ignoring PM request...\n"); 138 return Status; 139 } 140 141 /* Does the device support power management at all? */ 142 if (!(DeviceExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) 143 { 144 /* Get the PM capabilities register */ 145 CapsOffset = PciReadDeviceCapability(DeviceExtension, 146 DeviceExtension->CapabilitiesPtr, 147 PCI_CAPABILITY_ID_POWER_MANAGEMENT, 148 &PmCaps.Header, 149 sizeof(PCI_PM_CAPABILITY)); 150 ASSERT(CapsOffset); 151 ASSERT(DeviceState != PowerDeviceUnspecified); 152 153 /* Check if the device is being powered up */ 154 if (DeviceState == PowerDeviceD0) 155 { 156 /* Set full power state */ 157 PmCaps.PMCSR.ControlStatus.PowerState = 0; 158 159 /* Check if the device supports Cold-D3 poweroff */ 160 if (PmCaps.PMC.Capabilities.Support.PMED3Cold) 161 { 162 /* If there was a pending PME, clear it */ 163 PmCaps.PMCSR.ControlStatus.PMEStatus = 1; 164 } 165 } 166 else 167 { 168 /* Otherwise, just set the new power state, converting from NT */ 169 PmCaps.PMCSR.ControlStatus.PowerState = DeviceState - 1; 170 } 171 172 /* Write the new power state in the PMCSR */ 173 PciWriteDeviceConfig(DeviceExtension, 174 &PmCaps.PMCSR, 175 CapsOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR), 176 sizeof(PCI_PMCSR)); 177 178 /* Now wait for the change to "stick" based on the spec-mandated time */ 179 Status = PciStallForPowerChange(DeviceExtension, DeviceState, CapsOffset); 180 if (!NT_SUCCESS(Status)) return Status; 181 } 182 else 183 { 184 /* Nothing to do! */ 185 DPRINT1("No PM on this device, ignoring request\n"); 186 } 187 188 /* Check if new resources have to be assigned */ 189 if (IrpSet) 190 { 191 /* Check if the new device state is lower (higher power) than now */ 192 if (DeviceState < DeviceExtension->PowerState.CurrentDeviceState) 193 { 194 /* We would normally re-assign resources after powerup */ 195 UNIMPLEMENTED_DBGBREAK(); 196 Status = STATUS_NOT_IMPLEMENTED; 197 } 198 } 199 200 /* Return the power state change status */ 201 return Status; 202 } 203 204 NTSTATUS 205 NTAPI 206 PciFdoWaitWake(IN PIRP Irp, 207 IN PIO_STACK_LOCATION IoStackLocation, 208 IN PPCI_FDO_EXTENSION DeviceExtension) 209 { 210 UNREFERENCED_PARAMETER(Irp); 211 UNREFERENCED_PARAMETER(IoStackLocation); 212 UNREFERENCED_PARAMETER(DeviceExtension); 213 214 UNIMPLEMENTED; 215 while (TRUE); 216 return STATUS_NOT_SUPPORTED; 217 } 218 219 NTSTATUS 220 NTAPI 221 PciFdoSetPowerState(IN PIRP Irp, 222 IN PIO_STACK_LOCATION IoStackLocation, 223 IN PPCI_FDO_EXTENSION DeviceExtension) 224 { 225 UNREFERENCED_PARAMETER(Irp); 226 UNREFERENCED_PARAMETER(IoStackLocation); 227 UNREFERENCED_PARAMETER(DeviceExtension); 228 229 UNIMPLEMENTED; 230 while (TRUE); 231 return STATUS_NOT_SUPPORTED; 232 } 233 234 NTSTATUS 235 NTAPI 236 PciFdoIrpQueryPower(IN PIRP Irp, 237 IN PIO_STACK_LOCATION IoStackLocation, 238 IN PPCI_FDO_EXTENSION DeviceExtension) 239 { 240 UNREFERENCED_PARAMETER(Irp); 241 UNREFERENCED_PARAMETER(IoStackLocation); 242 UNREFERENCED_PARAMETER(DeviceExtension); 243 244 UNIMPLEMENTED; 245 while (TRUE); 246 return STATUS_NOT_SUPPORTED; 247 } 248 249 /* EOF */ 250