Sync with trunk (r49303)
[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 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 ULONG PciPowerDelayTable[PowerDeviceD3 * PowerDeviceD3] =
18 {
19 0, // D0 -> D0
20 0, // D1 -> D0
21 200, // D2 -> D0
22 10000, // D3 -> D0
23
24 0, // D0 -> D1
25 0, // D1 -> D1
26 200, // D2 -> D1
27 10000, // D3 -> D1
28
29 200, // D0 -> D2
30 200, // D1 -> D2
31 0, // D2 -> D2
32 10000, // D3 -> D2
33
34 10000, // D0 -> D3
35 10000, // D1 -> D3
36 10000, // D2 -> D3
37 0 // D3 -> D3
38 };
39
40 /* FUNCTIONS ******************************************************************/
41
42 NTSTATUS
43 NTAPI
44 PciStallForPowerChange(IN PPCI_PDO_EXTENSION PdoExtension,
45 IN DEVICE_POWER_STATE PowerState,
46 IN ULONG_PTR CapOffset)
47 {
48 ULONG PciState, TimeoutEntry, PmcsrOffset, TryCount;
49 PPCI_VERIFIER_DATA VerifierData;
50 LARGE_INTEGER Interval;
51 PCI_PMCSR Pmcsr;
52 KIRQL Irql;
53
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));
59
60 /* Save the current IRQL */
61 Irql = KeGetCurrentIrql();
62
63 /* Pick the expected timeout for this transition */
64 TimeoutEntry = PciPowerDelayTable[PowerState * PdoExtension->PowerState.CurrentDeviceState];
65
66 /* PCI power states are one less than NT power states */
67 PciState = PowerState - 1;
68
69 /* The state status is stored in the PMCSR offset */
70 PmcsrOffset = CapOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR);
71
72 /* Try changing the power state up to 100 times */
73 TryCount = 100;
74 while (--TryCount)
75 {
76 /* Check if this state transition will take time */
77 if (TimeoutEntry > 0)
78 {
79 /* Check if this is happening at high IRQL */
80 if (Irql >= DISPATCH_LEVEL)
81 {
82 /* Can't wait at high IRQL, stall the processor */
83 KeStallExecutionProcessor(TimeoutEntry);
84 }
85 else
86 {
87 /* Do a wait for the timeout specified instead */
88 Interval.QuadPart = -10 * TimeoutEntry;
89 Interval.QuadPart -= KeQueryTimeIncrement() - 1;
90 KeDelayExecutionThread(0, 0, &Interval);
91 }
92 }
93
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;
97
98 /* Try again, forcing a timeout of 1ms */
99 TimeoutEntry = 1000;
100 }
101
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,
111 "%DevObj%Ulong",
112 PdoExtension->PhysicalDeviceObject,
113 PciState);
114
115 return STATUS_DEVICE_PROTOCOL_ERROR;
116 }
117
118 NTSTATUS
119 NTAPI
120 PciSetPowerManagedDevicePowerState(IN PPCI_PDO_EXTENSION DeviceExtension,
121 IN DEVICE_POWER_STATE DeviceState,
122 IN BOOLEAN IrpSet)
123 {
124 NTSTATUS Status;
125 PCI_PM_CAPABILITY PmCaps;
126 ULONG CapsOffset;
127
128 /* Assume success */
129 Status = STATUS_SUCCESS;
130
131 /* Check if this device can support low power states */
132 if (!(PciCanDisableDecodes(DeviceExtension, NULL, 0, TRUE)) &&
133 (DeviceState != PowerDeviceD0))
134 {
135 /* Simply return success, ignoring this request */
136 DPRINT1("Cannot disable decodes on this device, ignoring PM request...\n");
137 return Status;
138 }
139
140 /* Does the device support power management at all? */
141 if (!(DeviceExtension->HackFlags & PCI_HACK_NO_PM_CAPS))
142 {
143 /* Get the PM capabailities register */
144 CapsOffset = PciReadDeviceCapability(DeviceExtension,
145 DeviceExtension->CapabilitiesPtr,
146 PCI_CAPABILITY_ID_POWER_MANAGEMENT,
147 &PmCaps.Header,
148 sizeof(PCI_PM_CAPABILITY));
149 ASSERT(CapsOffset);
150 ASSERT(DeviceState != PowerDeviceUnspecified);
151
152 /* Check if the device is being powered up */
153 if (DeviceState == PowerDeviceD0)
154 {
155 /* Set full power state */
156 PmCaps.PMCSR.ControlStatus.PowerState = 0;
157
158 /* Check if the device supports Cold-D3 poweroff */
159 if (PmCaps.PMC.Capabilities.Support.PMED3Cold)
160 {
161 /* If there was a pending PME, clear it */
162 PmCaps.PMCSR.ControlStatus.PMEStatus = 1;
163 }
164 }
165 else
166 {
167 /* Otherwise, just set the new power state, converting from NT */
168 PmCaps.PMCSR.ControlStatus.PowerState = DeviceState - 1;
169 }
170
171 /* Write the new power state in the PMCSR */
172 PciWriteDeviceConfig(DeviceExtension,
173 &PmCaps.PMCSR,
174 CapsOffset + FIELD_OFFSET(PCI_PM_CAPABILITY, PMCSR),
175 sizeof(PCI_PMCSR));
176
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;
180 }
181 else
182 {
183 /* Nothing to do! */
184 DPRINT1("No PM on this device, ignoring request\n");
185 }
186
187 /* Check if new resources have to be assigned */
188 if (IrpSet)
189 {
190 /* Check if the new device state is lower (higher power) than now */
191 if (DeviceState < DeviceExtension->PowerState.CurrentDeviceState)
192 {
193 /* We would normally re-assign resources after powerup */
194 UNIMPLEMENTED;
195 while (TRUE);
196 }
197 }
198
199 /* Return the power state change status */
200 return Status;
201 }
202
203 NTSTATUS
204 NTAPI
205 PciFdoWaitWake(IN PIRP Irp,
206 IN PIO_STACK_LOCATION IoStackLocation,
207 IN PPCI_FDO_EXTENSION DeviceExtension)
208 {
209 UNIMPLEMENTED;
210 while (TRUE);
211 return STATUS_NOT_SUPPORTED;
212 }
213
214 NTSTATUS
215 NTAPI
216 PciFdoSetPowerState(IN PIRP Irp,
217 IN PIO_STACK_LOCATION IoStackLocation,
218 IN PPCI_FDO_EXTENSION DeviceExtension)
219 {
220 UNIMPLEMENTED;
221 while (TRUE);
222 return STATUS_NOT_SUPPORTED;
223 }
224
225 NTSTATUS
226 NTAPI
227 PciFdoIrpQueryPower(IN PIRP Irp,
228 IN PIO_STACK_LOCATION IoStackLocation,
229 IN PPCI_FDO_EXTENSION DeviceExtension)
230 {
231 UNIMPLEMENTED;
232 while (TRUE);
233 return STATUS_NOT_SUPPORTED;
234 }
235
236 /* EOF */