Sync with trunk r63430.
[reactos.git] / drivers / bus / pcix / power.c
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(0, 0, &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 capabailities 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 */