xref: /reactos/drivers/bus/pcix/power.c (revision f986527d)
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(KernelMode, FALSE, &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 capabilities 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 */
250