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 *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 ULONG PciPowerDelayTable
[PowerDeviceD3
* PowerDeviceD3
] =
40 /* FUNCTIONS ******************************************************************/
44 PciStallForPowerChange(IN PPCI_PDO_EXTENSION PdoExtension
,
45 IN DEVICE_POWER_STATE PowerState
,
46 IN ULONG_PTR CapOffset
)
48 ULONG PciState
, TimeoutEntry
, PmcsrOffset
, TryCount
;
49 PPCI_VERIFIER_DATA VerifierData
;
50 LARGE_INTEGER Interval
;
54 /* Make sure the power state is valid, and the device can support it */
55 ASSERT((PdoExtension
->PowerState
.CurrentDeviceState
>= PowerDeviceD0
) &&
56 (PdoExtension
->PowerState
.CurrentDeviceState
<= PowerDeviceD3
));
57 ASSERT((PowerState
>= PowerDeviceD0
) && (PowerState
<= PowerDeviceD3
));
58 ASSERT(!(PdoExtension
->HackFlags
& PCI_HACK_NO_PM_CAPS
));
60 /* Save the current IRQL */
61 Irql
= KeGetCurrentIrql();
63 /* Pick the expected timeout for this transition */
64 TimeoutEntry
= PciPowerDelayTable
[PowerState
* PdoExtension
->PowerState
.CurrentDeviceState
];
66 /* PCI power states are one less than NT power states */
67 PciState
= PowerState
- 1;
69 /* The state status is stored in the PMCSR offset */
70 PmcsrOffset
= CapOffset
+ FIELD_OFFSET(PCI_PM_CAPABILITY
, PMCSR
);
72 /* Try changing the power state up to 100 times */
76 /* Check if this state transition will take time */
79 /* Check if this is happening at high IRQL */
80 if (Irql
>= DISPATCH_LEVEL
)
82 /* Can't wait at high IRQL, stall the processor */
83 KeStallExecutionProcessor(TimeoutEntry
);
87 /* Do a wait for the timeout specified instead */
88 Interval
.QuadPart
= -10 * TimeoutEntry
;
89 Interval
.QuadPart
-= KeQueryTimeIncrement() - 1;
90 KeDelayExecutionThread(0, 0, &Interval
);
94 /* Read the PMCSR and see if the state has changed */
95 PciReadDeviceConfig(PdoExtension
, &Pmcsr
, PmcsrOffset
, sizeof(PCI_PMCSR
));
96 if (Pmcsr
.PowerState
== PciState
) return STATUS_SUCCESS
;
98 /* Try again, forcing a timeout of 1ms */
102 /* Call verifier with this error */
103 VerifierData
= PciVerifierRetrieveFailureData(2);
104 ASSERT(VerifierData
);
105 VfFailDeviceNode(PdoExtension
->PhysicalDeviceObject
,
106 PCI_VERIFIER_DETECTED_VIOLATION
,
107 2, // The PMCSR register was not updated within the spec-mandated time.
108 VerifierData
->FailureClass
,
109 &VerifierData
->AssertionControl
,
110 VerifierData
->DebuggerMessageText
,
112 PdoExtension
->PhysicalDeviceObject
,
115 return STATUS_DEVICE_PROTOCOL_ERROR
;
120 PciSetPowerManagedDevicePowerState(IN PPCI_PDO_EXTENSION DeviceExtension
,
121 IN DEVICE_POWER_STATE DeviceState
,
125 PCI_PM_CAPABILITY PmCaps
;
129 Status
= STATUS_SUCCESS
;
131 /* Check if this device can support low power states */
132 if (!(PciCanDisableDecodes(DeviceExtension
, NULL
, 0, TRUE
)) &&
133 (DeviceState
!= PowerDeviceD0
))
135 /* Simply return success, ignoring this request */
136 DPRINT1("Cannot disable decodes on this device, ignoring PM request...\n");
140 /* Does the device support power management at all? */
141 if (!(DeviceExtension
->HackFlags
& PCI_HACK_NO_PM_CAPS
))
143 /* Get the PM capabailities register */
144 CapsOffset
= PciReadDeviceCapability(DeviceExtension
,
145 DeviceExtension
->CapabilitiesPtr
,
146 PCI_CAPABILITY_ID_POWER_MANAGEMENT
,
148 sizeof(PCI_PM_CAPABILITY
));
150 ASSERT(DeviceState
!= PowerDeviceUnspecified
);
152 /* Check if the device is being powered up */
153 if (DeviceState
== PowerDeviceD0
)
155 /* Set full power state */
156 PmCaps
.PMCSR
.ControlStatus
.PowerState
= 0;
158 /* Check if the device supports Cold-D3 poweroff */
159 if (PmCaps
.PMC
.Capabilities
.Support
.PMED3Cold
)
161 /* If there was a pending PME, clear it */
162 PmCaps
.PMCSR
.ControlStatus
.PMEStatus
= 1;
167 /* Otherwise, just set the new power state, converting from NT */
168 PmCaps
.PMCSR
.ControlStatus
.PowerState
= DeviceState
- 1;
171 /* Write the new power state in the PMCSR */
172 PciWriteDeviceConfig(DeviceExtension
,
174 CapsOffset
+ FIELD_OFFSET(PCI_PM_CAPABILITY
, PMCSR
),
177 /* Now wait for the change to "stick" based on the spec-mandated time */
178 Status
= PciStallForPowerChange(DeviceExtension
, DeviceState
, CapsOffset
);
179 if (!NT_SUCCESS(Status
)) return Status
;
184 DPRINT1("No PM on this device, ignoring request\n");
187 /* Check if new resources have to be assigned */
190 /* Check if the new device state is lower (higher power) than now */
191 if (DeviceState
< DeviceExtension
->PowerState
.CurrentDeviceState
)
193 /* We would normally re-assign resources after powerup */
195 ASSERT(FALSE
); // while (TRUE);
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
)
212 return STATUS_NOT_SUPPORTED
;
217 PciFdoSetPowerState(IN PIRP Irp
,
218 IN PIO_STACK_LOCATION IoStackLocation
,
219 IN PPCI_FDO_EXTENSION DeviceExtension
)
223 return STATUS_NOT_SUPPORTED
;
228 PciFdoIrpQueryPower(IN PIRP Irp
,
229 IN PIO_STACK_LOCATION IoStackLocation
,
230 IN PPCI_FDO_EXTENSION DeviceExtension
)
234 return STATUS_NOT_SUPPORTED
;