xref: /reactos/drivers/network/dd/dc21x4/power.c (revision 114ef5f1)
1 /*
2  * PROJECT:     ReactOS DC21x4 Driver
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Power management
5  * COPYRIGHT:   Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "dc21x4.h"
11 
12 #include <debug.h>
13 
14 /* FUNCTIONS ******************************************************************/
15 
16 static
17 CODE_SEG("PAGE")
18 VOID
19 DcDownloadPatternFilter(
20     _In_ PDC21X4_ADAPTER Adapter,
21     _In_ PDC_PATTERN_FILTER_BLOCK FilterBlock)
22 {
23     ULONG i;
24 
25     PAGED_CODE();
26 
27     for (i = 0; i < sizeof(*FilterBlock) / sizeof(ULONG); ++i)
28     {
29         DC_WRITE(Adapter, DcCsr1_WakeUpFilter, FilterBlock->AsULONG[i]);
30     }
31 }
32 
33 static
34 CODE_SEG("PAGE")
35 VOID
36 DcSetupWakeUpFilter(
37     _In_ PDC21X4_ADAPTER Adapter)
38 {
39     DC_PATTERN_FILTER_BLOCK FilterBlock;
40 
41     PAGED_CODE();
42 
43     /* Save the address filtering */
44     NdisMoveMemory(Adapter->SetupFrameSaved, Adapter->SetupFrame, DC_SETUP_FRAME_SIZE);
45 
46     NdisZeroMemory(&FilterBlock, sizeof(FilterBlock));
47 
48     // TODO: Convert NDIS patterns to HW filter and prepare a setup frame
49 
50     DcDownloadPatternFilter(Adapter, &FilterBlock);
51 }
52 
53 static
54 CODE_SEG("PAGE")
55 VOID
56 DcProgramWakeUpEvents(
57     _In_ PDC21X4_ADAPTER Adapter)
58 {
59     ULONG WakeUpControl;
60 
61     PAGED_CODE();
62 
63     /* Clear the wake-up events */
64     WakeUpControl = (DC_WAKE_UP_STATUS_LINK_CHANGE |
65                      DC_WAKE_UP_STATUS_MAGIC_PACKET |
66                      DC_WAKE_UP_CONTROL_PATTERN_MATCH);
67 
68     /* Convert NDIS flags to hardware-specific values */
69     if (Adapter->WakeUpFlags & NDIS_PNP_WAKE_UP_LINK_CHANGE)
70         WakeUpControl |= DC_WAKE_UP_CONTROL_LINK_CHANGE;
71     if (Adapter->WakeUpFlags & NDIS_PNP_WAKE_UP_MAGIC_PACKET)
72         WakeUpControl |= DC_WAKE_UP_CONTROL_MAGIC_PACKET;
73 #if 0 // TODO: Pattern matching is not yet supported
74     if (Adapter->WakeUpFlags & NDIS_PNP_WAKE_UP_PATTERN_MATCH)
75         WakeUpControl |= DC_WAKE_UP_CONTROL_PATTERN_MATCH;
76 #endif
77 
78     DC_WRITE(Adapter, DcCsr2_WakeUpControl, WakeUpControl);
79 }
80 
81 static
82 CODE_SEG("PAGE")
83 VOID
84 DcPowerDown(
85     _In_ PDC21X4_ADAPTER Adapter)
86 {
87     ULONG SiaState, SerialInterface;
88 
89     PAGED_CODE();
90 
91     /* Stop the receive and transmit processes */
92     DcStopAdapter(Adapter, FALSE);
93 
94     Adapter->CurrentInterruptMask = 0;
95 
96     /* Enable the link integrity test bit */
97     switch (Adapter->MediaNumber)
98     {
99         case MEDIA_AUI:
100         case MEDIA_BNC:
101         case MEDIA_HMR:
102         {
103             SiaState = DC_READ(Adapter, DcCsr14_SiaTxRx);
104             if (!(SiaState & DC_SIA_TXRX_LINK_TEST))
105             {
106                 SiaState |= DC_SIA_TXRX_LINK_TEST;
107                 DC_WRITE(Adapter, DcCsr14_SiaTxRx, SiaState);
108             }
109             break;
110         }
111 
112         default:
113             break;
114     }
115 
116     /* Clear the MDC bit */
117     SerialInterface = DC_READ(Adapter, DcCsr9_SerialInterface);
118     if (SerialInterface & DC_SERIAL_MII_MDC)
119     {
120         SerialInterface &= ~DC_SERIAL_MII_MDC;
121         DC_WRITE(Adapter, DcCsr9_SerialInterface, SerialInterface);
122     }
123 
124     /* Unprotect PM access */
125     DC_WRITE(Adapter, DcCsr0_BusMode, Adapter->BusMode | DC_BUS_MODE_ON_NOW_UNLOCK);
126 
127     /* Program the requested WOL events */
128     DcSetupWakeUpFilter(Adapter);
129     DcProgramWakeUpEvents(Adapter);
130 
131     /* Protect PM access */
132     DC_WRITE(Adapter, DcCsr0_BusMode, Adapter->BusMode);
133 }
134 
135 static
136 CODE_SEG("PAGE")
137 VOID
138 DcPowerUp(
139     _In_ PDC21X4_ADAPTER Adapter)
140 {
141     PAGED_CODE();
142 
143     /* Restore the address filtering */
144     NdisMoveMemory(Adapter->SetupFrame, Adapter->SetupFrameSaved, DC_SETUP_FRAME_SIZE);
145 
146     /* Re-initialize the chip to leave D3 state */
147     if (Adapter->PrevPowerState == NdisDeviceStateD3)
148     {
149         NT_VERIFY(DcSetupAdapter(Adapter) == NDIS_STATUS_SUCCESS);
150     }
151     else
152     {
153         /* Start the transmit process */
154         Adapter->OpMode |= DC_OPMODE_TX_ENABLE;
155         DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode);
156 
157         /* Load the address recognition RAM */
158         NT_VERIFY(DcSetupFrameDownload(Adapter, TRUE) == TRUE);
159     }
160 
161     DcStartAdapter(Adapter);
162 }
163 
164 CODE_SEG("PAGE")
165 VOID
166 NTAPI
167 DcPowerWorker(
168     _In_ PNDIS_WORK_ITEM WorkItem,
169     _In_opt_ PVOID Context)
170 {
171     PDC21X4_ADAPTER Adapter = Context;
172 
173     UNREFERENCED_PARAMETER(WorkItem);
174 
175     PAGED_CODE();
176 
177     if (Adapter->PowerState == NdisDeviceStateD0)
178     {
179         DcPowerUp(Adapter);
180     }
181     else
182     {
183         DcPowerDown(Adapter);
184     }
185     Adapter->PrevPowerState = Adapter->PowerState;
186 
187     NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS);
188 }
189 
190 NDIS_STATUS
191 DcSetPower(
192     _In_ PDC21X4_ADAPTER Adapter,
193     _In_ NDIS_DEVICE_POWER_STATE PowerState)
194 {
195     INFO("Power state %u\n", PowerState);
196 
197     Adapter->PowerState = PowerState;
198 
199     NdisScheduleWorkItem(&Adapter->PowerWorkItem);
200 
201     return NDIS_STATUS_PENDING;
202 }
203 
204 NDIS_STATUS
205 DcRemoveWakeUpPattern(
206     _In_ PDC21X4_ADAPTER Adapter,
207     _In_ PNDIS_PM_PACKET_PATTERN PmPattern)
208 {
209     // TODO: Not implemented
210     ERR("FIXME: Not implemented\n");
211     return NDIS_STATUS_NOT_SUPPORTED;
212 }
213 
214 NDIS_STATUS
215 DcAddWakeUpPattern(
216     _In_ PDC21X4_ADAPTER Adapter,
217     _In_ PNDIS_PM_PACKET_PATTERN PmPattern)
218 {
219     // TODO: Not implemented
220     ERR("FIXME: Not implemented\n");
221     return NDIS_STATUS_NOT_SUPPORTED;
222 }
223 
224 VOID
225 DcPowerSave(
226     _In_ PDC21X4_ADAPTER Adapter,
227     _In_ BOOLEAN Enable)
228 {
229     ULONG ConfigValue;
230 
231     if (!(Adapter->Features & DC_HAS_POWER_SAVING))
232         return;
233 
234     NdisReadPciSlotInformation(Adapter->AdapterHandle,
235                                0,
236                                DC_PCI_DEVICE_CONFIG,
237                                &ConfigValue,
238                                sizeof(ConfigValue));
239 
240     ConfigValue &= ~DC_PCI_DEVICE_CONFIG_SLEEP;
241 
242     if (Enable)
243         ConfigValue |= DC_PCI_DEVICE_CONFIG_SNOOZE;
244     else
245         ConfigValue &= ~DC_PCI_DEVICE_CONFIG_SNOOZE;
246 
247     NdisWritePciSlotInformation(Adapter->AdapterHandle,
248                                 0,
249                                 DC_PCI_DEVICE_CONFIG,
250                                 &ConfigValue,
251                                 sizeof(ConfigValue));
252 }
253