mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
249 lines
7.8 KiB
C
249 lines
7.8 KiB
C
/*
|
|
* PROJECT: ReactOS PCI Bus Driver
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: drivers/bus/pci/power.c
|
|
* PURPOSE: Bus/Device Power Management
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <pci.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
ULONG PciPowerDelayTable[PowerDeviceD3 * PowerDeviceD3] =
|
|
{
|
|
0, // D0 -> D0
|
|
0, // D1 -> D0
|
|
200, // D2 -> D0
|
|
10000, // D3 -> D0
|
|
|
|
0, // D0 -> D1
|
|
0, // D1 -> D1
|
|
200, // D2 -> D1
|
|
10000, // D3 -> D1
|
|
|
|
200, // D0 -> D2
|
|
200, // D1 -> D2
|
|
0, // D2 -> D2
|
|
10000, // D3 -> D2
|
|
|
|
10000, // D0 -> D3
|
|
10000, // D1 -> D3
|
|
10000, // D2 -> D3
|
|
0 // D3 -> D3
|
|
};
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciStallForPowerChange(IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN DEVICE_POWER_STATE PowerState,
|
|
IN ULONG_PTR CapOffset)
|
|
{
|
|
ULONG PciState, TimeoutEntry, PmcsrOffset, TryCount;
|
|
PPCI_VERIFIER_DATA VerifierData;
|
|
LARGE_INTEGER Interval;
|
|
PCI_PMCSR Pmcsr;
|
|
KIRQL Irql;
|
|
|
|
/* Make sure the power state is valid, and the device can support it */
|
|
ASSERT((PdoExtension->PowerState.CurrentDeviceState >= PowerDeviceD0) &&
|
|
(PdoExtension->PowerState.CurrentDeviceState <= PowerDeviceD3));
|
|
ASSERT((PowerState >= PowerDeviceD0) && (PowerState <= PowerDeviceD3));
|
|
ASSERT(!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS));
|
|
|
|
/* Save the current IRQL */
|
|
Irql = KeGetCurrentIrql();
|
|
|
|
/* Pick the expected timeout for this transition */
|
|
TimeoutEntry = PciPowerDelayTable[PowerState * PdoExtension->PowerState.CurrentDeviceState];
|
|
|
|
/* PCI power states are one less than NT power states */
|
|
PciState = PowerState - 1;
|
|
|
|
/* The state status is stored in the PMCSR offset */
|
|
PmcsrOffset = CapOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR);
|
|
|
|
/* Try changing the power state up to 100 times */
|
|
TryCount = 100;
|
|
while (--TryCount)
|
|
{
|
|
/* Check if this state transition will take time */
|
|
if (TimeoutEntry > 0)
|
|
{
|
|
/* Check if this is happening at high IRQL */
|
|
if (Irql >= DISPATCH_LEVEL)
|
|
{
|
|
/* Can't wait at high IRQL, stall the processor */
|
|
KeStallExecutionProcessor(TimeoutEntry);
|
|
}
|
|
else
|
|
{
|
|
/* Do a wait for the timeout specified instead */
|
|
Interval.QuadPart = -10 * TimeoutEntry;
|
|
Interval.QuadPart -= KeQueryTimeIncrement() - 1;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
|
|
}
|
|
}
|
|
|
|
/* Read the PMCSR and see if the state has changed */
|
|
PciReadDeviceConfig(PdoExtension, &Pmcsr, PmcsrOffset, sizeof(PCI_PMCSR));
|
|
if (Pmcsr.PowerState == PciState) return STATUS_SUCCESS;
|
|
|
|
/* Try again, forcing a timeout of 1ms */
|
|
TimeoutEntry = 1000;
|
|
}
|
|
|
|
/* Call verifier with this error */
|
|
VerifierData = PciVerifierRetrieveFailureData(2);
|
|
ASSERT(VerifierData);
|
|
VfFailDeviceNode(PdoExtension->PhysicalDeviceObject,
|
|
PCI_VERIFIER_DETECTED_VIOLATION,
|
|
2, // The PMCSR register was not updated within the spec-mandated time.
|
|
VerifierData->FailureClass,
|
|
&VerifierData->AssertionControl,
|
|
VerifierData->DebuggerMessageText,
|
|
"%DevObj%Ulong",
|
|
PdoExtension->PhysicalDeviceObject,
|
|
PciState);
|
|
|
|
return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciSetPowerManagedDevicePowerState(IN PPCI_PDO_EXTENSION DeviceExtension,
|
|
IN DEVICE_POWER_STATE DeviceState,
|
|
IN BOOLEAN IrpSet)
|
|
{
|
|
NTSTATUS Status;
|
|
PCI_PM_CAPABILITY PmCaps;
|
|
ULONG CapsOffset;
|
|
|
|
/* Assume success */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Check if this device can support low power states */
|
|
if (!(PciCanDisableDecodes(DeviceExtension, NULL, 0, TRUE)) &&
|
|
(DeviceState != PowerDeviceD0))
|
|
{
|
|
/* Simply return success, ignoring this request */
|
|
DPRINT1("Cannot disable decodes on this device, ignoring PM request...\n");
|
|
return Status;
|
|
}
|
|
|
|
/* Does the device support power management at all? */
|
|
if (!(DeviceExtension->HackFlags & PCI_HACK_NO_PM_CAPS))
|
|
{
|
|
/* Get the PM capabilities register */
|
|
CapsOffset = PciReadDeviceCapability(DeviceExtension,
|
|
DeviceExtension->CapabilitiesPtr,
|
|
PCI_CAPABILITY_ID_POWER_MANAGEMENT,
|
|
&PmCaps.Header,
|
|
sizeof(PCI_PM_CAPABILITY));
|
|
ASSERT(CapsOffset);
|
|
ASSERT(DeviceState != PowerDeviceUnspecified);
|
|
|
|
/* Check if the device is being powered up */
|
|
if (DeviceState == PowerDeviceD0)
|
|
{
|
|
/* Set full power state */
|
|
PmCaps.PMCSR.ControlStatus.PowerState = 0;
|
|
|
|
/* Check if the device supports Cold-D3 poweroff */
|
|
if (PmCaps.PMC.Capabilities.Support.PMED3Cold)
|
|
{
|
|
/* If there was a pending PME, clear it */
|
|
PmCaps.PMCSR.ControlStatus.PMEStatus = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, just set the new power state, converting from NT */
|
|
PmCaps.PMCSR.ControlStatus.PowerState = DeviceState - 1;
|
|
}
|
|
|
|
/* Write the new power state in the PMCSR */
|
|
PciWriteDeviceConfig(DeviceExtension,
|
|
&PmCaps.PMCSR,
|
|
CapsOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR),
|
|
sizeof(PCI_PMCSR));
|
|
|
|
/* Now wait for the change to "stick" based on the spec-mandated time */
|
|
Status = PciStallForPowerChange(DeviceExtension, DeviceState, CapsOffset);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
}
|
|
else
|
|
{
|
|
/* Nothing to do! */
|
|
DPRINT1("No PM on this device, ignoring request\n");
|
|
}
|
|
|
|
/* Check if new resources have to be assigned */
|
|
if (IrpSet)
|
|
{
|
|
/* Check if the new device state is lower (higher power) than now */
|
|
if (DeviceState < DeviceExtension->PowerState.CurrentDeviceState)
|
|
{
|
|
/* We would normally re-assign resources after powerup */
|
|
UNIMPLEMENTED_DBGBREAK();
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
/* Return the power state change status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciFdoWaitWake(IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IoStackLocation,
|
|
IN PPCI_FDO_EXTENSION DeviceExtension)
|
|
{
|
|
UNREFERENCED_PARAMETER(Irp);
|
|
UNREFERENCED_PARAMETER(IoStackLocation);
|
|
UNREFERENCED_PARAMETER(DeviceExtension);
|
|
|
|
UNIMPLEMENTED;
|
|
while (TRUE);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciFdoSetPowerState(IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IoStackLocation,
|
|
IN PPCI_FDO_EXTENSION DeviceExtension)
|
|
{
|
|
UNREFERENCED_PARAMETER(Irp);
|
|
UNREFERENCED_PARAMETER(IoStackLocation);
|
|
UNREFERENCED_PARAMETER(DeviceExtension);
|
|
|
|
UNIMPLEMENTED;
|
|
while (TRUE);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciFdoIrpQueryPower(IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IoStackLocation,
|
|
IN PPCI_FDO_EXTENSION DeviceExtension)
|
|
{
|
|
UNREFERENCED_PARAMETER(Irp);
|
|
UNREFERENCED_PARAMETER(IoStackLocation);
|
|
UNREFERENCED_PARAMETER(DeviceExtension);
|
|
|
|
UNIMPLEMENTED;
|
|
while (TRUE);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
/* EOF */
|