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
9 /* INCLUDES *******************************************************************/
16 /* GLOBALS ********************************************************************/
18 ULONG PciPowerDelayTable
[PowerDeviceD3
* PowerDeviceD3
] =
41 /* FUNCTIONS ******************************************************************/
45 PciStallForPowerChange(IN PPCI_PDO_EXTENSION PdoExtension
,
46 IN DEVICE_POWER_STATE PowerState
,
47 IN ULONG_PTR CapOffset
)
49 ULONG PciState
, TimeoutEntry
, PmcsrOffset
, TryCount
;
50 PPCI_VERIFIER_DATA VerifierData
;
51 LARGE_INTEGER Interval
;
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
));
61 /* Save the current IRQL */
62 Irql
= KeGetCurrentIrql();
64 /* Pick the expected timeout for this transition */
65 TimeoutEntry
= PciPowerDelayTable
[PowerState
* PdoExtension
->PowerState
.CurrentDeviceState
];
67 /* PCI power states are one less than NT power states */
68 PciState
= PowerState
- 1;
70 /* The state status is stored in the PMCSR offset */
71 PmcsrOffset
= CapOffset
+ FIELD_OFFSET(PCI_PM_CAPABILITY
, PMCSR
);
73 /* Try changing the power state up to 100 times */
77 /* Check if this state transition will take time */
80 /* Check if this is happening at high IRQL */
81 if (Irql
>= DISPATCH_LEVEL
)
83 /* Can't wait at high IRQL, stall the processor */
84 KeStallExecutionProcessor(TimeoutEntry
);
88 /* Do a wait for the timeout specified instead */
89 Interval
.QuadPart
= -10 * TimeoutEntry
;
90 Interval
.QuadPart
-= KeQueryTimeIncrement() - 1;
91 KeDelayExecutionThread(0, 0, &Interval
);
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
;
99 /* Try again, forcing a timeout of 1ms */
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
,
113 PdoExtension
->PhysicalDeviceObject
,
116 return STATUS_DEVICE_PROTOCOL_ERROR
;
121 PciSetPowerManagedDevicePowerState(IN PPCI_PDO_EXTENSION DeviceExtension
,
122 IN DEVICE_POWER_STATE DeviceState
,
126 PCI_PM_CAPABILITY PmCaps
;
130 Status
= STATUS_SUCCESS
;
132 /* Check if this device can support low power states */
133 if (!(PciCanDisableDecodes(DeviceExtension
, NULL
, 0, TRUE
)) &&
134 (DeviceState
!= PowerDeviceD0
))
136 /* Simply return success, ignoring this request */
137 DPRINT1("Cannot disable decodes on this device, ignoring PM request...\n");
141 /* Does the device support power management at all? */
142 if (!(DeviceExtension
->HackFlags
& PCI_HACK_NO_PM_CAPS
))
144 /* Get the PM capabailities register */
145 CapsOffset
= PciReadDeviceCapability(DeviceExtension
,
146 DeviceExtension
->CapabilitiesPtr
,
147 PCI_CAPABILITY_ID_POWER_MANAGEMENT
,
149 sizeof(PCI_PM_CAPABILITY
));
151 ASSERT(DeviceState
!= PowerDeviceUnspecified
);
153 /* Check if the device is being powered up */
154 if (DeviceState
== PowerDeviceD0
)
156 /* Set full power state */
157 PmCaps
.PMCSR
.ControlStatus
.PowerState
= 0;
159 /* Check if the device supports Cold-D3 poweroff */
160 if (PmCaps
.PMC
.Capabilities
.Support
.PMED3Cold
)
162 /* If there was a pending PME, clear it */
163 PmCaps
.PMCSR
.ControlStatus
.PMEStatus
= 1;
168 /* Otherwise, just set the new power state, converting from NT */
169 PmCaps
.PMCSR
.ControlStatus
.PowerState
= DeviceState
- 1;
172 /* Write the new power state in the PMCSR */
173 PciWriteDeviceConfig(DeviceExtension
,
175 CapsOffset
+ FIELD_OFFSET(PCI_PM_CAPABILITY
, PMCSR
),
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
;
185 DPRINT1("No PM on this device, ignoring request\n");
188 /* Check if new resources have to be assigned */
191 /* Check if the new device state is lower (higher power) than now */
192 if (DeviceState
< DeviceExtension
->PowerState
.CurrentDeviceState
)
194 /* We would normally re-assign resources after powerup */
195 UNIMPLEMENTED_DBGBREAK();
196 Status
= STATUS_NOT_IMPLEMENTED
;
200 /* Return the power state change status */
206 PciFdoWaitWake(IN PIRP Irp
,
207 IN PIO_STACK_LOCATION IoStackLocation
,
208 IN PPCI_FDO_EXTENSION DeviceExtension
)
210 UNREFERENCED_PARAMETER(Irp
);
211 UNREFERENCED_PARAMETER(IoStackLocation
);
212 UNREFERENCED_PARAMETER(DeviceExtension
);
216 return STATUS_NOT_SUPPORTED
;
221 PciFdoSetPowerState(IN PIRP Irp
,
222 IN PIO_STACK_LOCATION IoStackLocation
,
223 IN PPCI_FDO_EXTENSION DeviceExtension
)
225 UNREFERENCED_PARAMETER(Irp
);
226 UNREFERENCED_PARAMETER(IoStackLocation
);
227 UNREFERENCED_PARAMETER(DeviceExtension
);
231 return STATUS_NOT_SUPPORTED
;
236 PciFdoIrpQueryPower(IN PIRP Irp
,
237 IN PIO_STACK_LOCATION IoStackLocation
,
238 IN PPCI_FDO_EXTENSION DeviceExtension
)
240 UNREFERENCED_PARAMETER(Irp
);
241 UNREFERENCED_PARAMETER(IoStackLocation
);
242 UNREFERENCED_PARAMETER(DeviceExtension
);
246 return STATUS_NOT_SUPPORTED
;