xref: /reactos/drivers/network/dd/e1000/hardware.c (revision d9b0601c)
1 /*
2  * PROJECT:     ReactOS Intel PRO/1000 Driver
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Hardware specific functions
5  * COPYRIGHT:   Copyright 2018 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include "nic.h"
9 
10 #include <debug.h>
11 
12 
13 static USHORT SupportedDevices[] =
14 {
15     0x100f,     // Intel 82545EM (VMWare E1000)
16 };
17 
18 
19 static ULONG E1000WriteFlush(IN PE1000_ADAPTER Adapter)
20 {
21     volatile ULONG Value;
22 
23     NdisReadRegisterUlong(Adapter->IoBase + E1000_REG_STATUS, &Value);
24     return Value;
25 }
26 
27 static VOID E1000WriteUlong(IN PE1000_ADAPTER Adapter, IN ULONG Address, IN ULONG Value)
28 {
29     NdisWriteRegisterUlong((PULONG)(Adapter->IoBase + Address), Value);
30 }
31 
32 static VOID E1000ReadUlong(IN PE1000_ADAPTER Adapter, IN ULONG Address, OUT PULONG Value)
33 {
34     NdisReadRegisterUlong((PULONG)(Adapter->IoBase + Address), Value);
35 }
36 
37 static VOID E1000WriteIoUlong(IN PE1000_ADAPTER Adapter, IN ULONG Address, IN ULONG Value)
38 {
39     NdisRawWritePortUlong((PULONG)(Adapter->IoPort), Address);
40     E1000WriteFlush(Adapter);
41     NdisRawWritePortUlong((PULONG)(Adapter->IoPort + 4), Value);
42 }
43 
44 static BOOLEAN E1000ReadMdic(IN PE1000_ADAPTER Adapter, IN ULONG Address, USHORT *Result)
45 {
46     ULONG ResultAddress;
47     ULONG Mdic;
48     UINT n;
49 
50     if (Address > MAX_PHY_REG_ADDRESS)
51     {
52         NDIS_DbgPrint(MIN_TRACE, ("PHY Address %d is invalid\n", Address));
53         return 1;
54     }
55 
56     Mdic = (Address << E1000_MDIC_REGADD_SHIFT);
57     Mdic |= (E1000_MDIC_PHYADD_GIGABIT << E1000_MDIC_PHYADD_SHIFT);
58     Mdic |= E1000_MDIC_OP_READ;
59 
60     E1000WriteUlong(Adapter, E1000_REG_MDIC, Mdic);
61 
62     for (n = 0; n < MAX_PHY_READ_ATTEMPTS; n++)
63     {
64         NdisStallExecution(50);
65         E1000ReadUlong(Adapter, E1000_REG_MDIC, &Mdic);
66         if (Mdic & E1000_MDIC_R)
67             break;
68     }
69     if (!(Mdic & E1000_MDIC_R))
70     {
71         NDIS_DbgPrint(MIN_TRACE, ("MDI Read incomplete\n"));
72         return FALSE;
73     }
74     if (Mdic & E1000_MDIC_E)
75     {
76         NDIS_DbgPrint(MIN_TRACE, ("MDI Read error\n"));
77         return FALSE;
78     }
79 
80     ResultAddress = (Mdic >> E1000_MDIC_REGADD_SHIFT) & MAX_PHY_REG_ADDRESS;
81 
82     if (ResultAddress!= Address)
83     {
84         /* Add locking? */
85         NDIS_DbgPrint(MIN_TRACE, ("MDI Read got wrong address (%d instead of %d)\n",
86                                   ResultAddress, Address));
87         return FALSE;
88     }
89     *Result = (USHORT) Mdic;
90     return TRUE;
91 }
92 
93 
94 static BOOLEAN E1000ReadEeprom(IN PE1000_ADAPTER Adapter, IN UCHAR Address, USHORT *Result)
95 {
96     UINT Value;
97     UINT n;
98 
99     E1000WriteUlong(Adapter, E1000_REG_EERD, E1000_EERD_START | ((UINT)Address << E1000_EERD_ADDR_SHIFT));
100 
101     for (n = 0; n < MAX_EEPROM_READ_ATTEMPTS; ++n)
102     {
103         NdisStallExecution(5);
104 
105         E1000ReadUlong(Adapter, E1000_REG_EERD, &Value);
106 
107         if (Value & E1000_EERD_DONE)
108             break;
109     }
110     if (!(Value & E1000_EERD_DONE))
111     {
112         NDIS_DbgPrint(MIN_TRACE, ("EEPROM Read incomplete\n"));
113         return FALSE;
114     }
115     *Result = (USHORT)(Value >> E1000_EERD_DATA_SHIFT);
116     return TRUE;
117 }
118 
119 BOOLEAN E1000ValidateNvmChecksum(IN PE1000_ADAPTER Adapter)
120 {
121     USHORT Checksum = 0, Data;
122     UINT n;
123 
124     /* 5.6.35 Checksum Word Calculation (Word 3Fh) */
125     for (n = 0; n <= E1000_NVM_REG_CHECKSUM; n++)
126     {
127         if (!E1000ReadEeprom(Adapter, n, &Data))
128         {
129             return FALSE;
130         }
131         Checksum += Data;
132     }
133 
134     if (Checksum != NVM_MAGIC_SUM)
135     {
136         NDIS_DbgPrint(MIN_TRACE, ("EEPROM has an invalid checksum of 0x%x\n", (ULONG)Checksum));
137         return FALSE;
138     }
139 
140     return TRUE;
141 }
142 
143 
144 BOOLEAN
145 NTAPI
146 NICRecognizeHardware(
147     IN PE1000_ADAPTER Adapter)
148 {
149     UINT n;
150     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
151 
152     if (Adapter->VendorID != HW_VENDOR_INTEL)
153     {
154         NDIS_DbgPrint(MIN_TRACE, ("Unknown vendor: 0x%x\n", Adapter->VendorID));
155         return FALSE;
156     }
157 
158     for (n = 0; n < ARRAYSIZE(SupportedDevices); ++n)
159     {
160         if (SupportedDevices[n] == Adapter->DeviceID)
161         {
162             return TRUE;
163         }
164     }
165 
166     NDIS_DbgPrint(MIN_TRACE, ("Unknown device: 0x%x\n", Adapter->DeviceID));
167 
168     return FALSE;
169 }
170 
171 NDIS_STATUS
172 NTAPI
173 NICInitializeAdapterResources(
174     IN PE1000_ADAPTER Adapter,
175     IN PNDIS_RESOURCE_LIST ResourceList)
176 {
177     UINT n;
178     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
179 
180     for (n = 0; n < ResourceList->Count; n++)
181     {
182         PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor = ResourceList->PartialDescriptors + n;
183 
184         switch (ResourceDescriptor->Type)
185         {
186         case CmResourceTypePort:
187             ASSERT(Adapter->IoPortAddress == 0);
188             ASSERT(ResourceDescriptor->u.Port.Start.HighPart == 0);
189 
190             Adapter->IoPortAddress = ResourceDescriptor->u.Port.Start.LowPart;
191             Adapter->IoPortLength = ResourceDescriptor->u.Port.Length;
192 
193             NDIS_DbgPrint(MID_TRACE, ("I/O port range is %p to %p\n",
194                                       Adapter->IoPortAddress,
195                                       Adapter->IoPortAddress + Adapter->IoPortLength));
196             break;
197         case CmResourceTypeInterrupt:
198             ASSERT(Adapter->InterruptVector == 0);
199             ASSERT(Adapter->InterruptLevel == 0);
200 
201             Adapter->InterruptVector = ResourceDescriptor->u.Interrupt.Vector;
202             Adapter->InterruptLevel = ResourceDescriptor->u.Interrupt.Level;
203             Adapter->InterruptShared = (ResourceDescriptor->ShareDisposition == CmResourceShareShared);
204             Adapter->InterruptFlags = ResourceDescriptor->Flags;
205 
206             NDIS_DbgPrint(MID_TRACE, ("IRQ vector is %d\n", Adapter->InterruptVector));
207             break;
208         case CmResourceTypeMemory:
209             /* Internal registers and memories (including PHY) */
210             if (ResourceDescriptor->u.Memory.Length ==  (128 * 1024))
211             {
212                 ASSERT(Adapter->IoAddress.LowPart == 0);
213                 ASSERT(ResourceDescriptor->u.Port.Start.HighPart == 0);
214 
215 
216                 Adapter->IoAddress.QuadPart = ResourceDescriptor->u.Memory.Start.QuadPart;
217                 Adapter->IoLength = ResourceDescriptor->u.Memory.Length;
218                 NDIS_DbgPrint(MID_TRACE, ("Memory range is %I64x to %I64x\n",
219                                           Adapter->IoAddress.QuadPart,
220                                           Adapter->IoAddress.QuadPart + Adapter->IoLength));
221             }
222             break;
223 
224         default:
225             NDIS_DbgPrint(MIN_TRACE, ("Unrecognized resource type: 0x%x\n", ResourceDescriptor->Type));
226             break;
227         }
228     }
229 
230     if (Adapter->IoAddress.QuadPart == 0 || Adapter->IoPortAddress == 0 || Adapter->InterruptVector == 0)
231     {
232         NDIS_DbgPrint(MIN_TRACE, ("Adapter didn't receive enough resources\n"));
233         return NDIS_STATUS_RESOURCES;
234     }
235 
236     return NDIS_STATUS_SUCCESS;
237 }
238 
239 NDIS_STATUS
240 NTAPI
241 NICAllocateIoResources(
242     IN PE1000_ADAPTER Adapter)
243 {
244     NDIS_STATUS Status;
245     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
246 
247     Status = NdisMRegisterIoPortRange((PVOID*)&Adapter->IoPort,
248                                       Adapter->AdapterHandle,
249                                       Adapter->IoPortAddress,
250                                       Adapter->IoPortLength);
251     if (Status != NDIS_STATUS_SUCCESS)
252     {
253         NDIS_DbgPrint(MIN_TRACE, ("Unable to register IO port range (0x%x)\n", Status));
254         return NDIS_STATUS_RESOURCES;
255     }
256 
257     Status = NdisMMapIoSpace((PVOID*)&Adapter->IoBase,
258                              Adapter->AdapterHandle,
259                              Adapter->IoAddress,
260                              Adapter->IoLength);
261 
262     return NDIS_STATUS_SUCCESS;
263 }
264 
265 NDIS_STATUS
266 NTAPI
267 NICRegisterInterrupts(
268     IN PE1000_ADAPTER Adapter)
269 {
270     NDIS_STATUS Status;
271     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
272 
273     Status = NdisMRegisterInterrupt(&Adapter->Interrupt,
274                                     Adapter->AdapterHandle,
275                                     Adapter->InterruptVector,
276                                     Adapter->InterruptLevel,
277                                     TRUE, // We always want ISR calls
278                                     Adapter->InterruptShared,
279                                     (Adapter->InterruptFlags & CM_RESOURCE_INTERRUPT_LATCHED) ?
280                                     NdisInterruptLatched : NdisInterruptLevelSensitive);
281 
282     if (Status == NDIS_STATUS_SUCCESS)
283     {
284         Adapter->InterruptRegistered = TRUE;
285     }
286 
287     return Status;
288 }
289 
290 NDIS_STATUS
291 NTAPI
292 NICUnregisterInterrupts(
293     IN PE1000_ADAPTER Adapter)
294 {
295     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
296 
297     if (Adapter->InterruptRegistered)
298     {
299         NdisMDeregisterInterrupt(&Adapter->Interrupt);
300         Adapter->InterruptRegistered = FALSE;
301     }
302 
303     return NDIS_STATUS_SUCCESS;
304 }
305 
306 NDIS_STATUS
307 NTAPI
308 NICReleaseIoResources(
309     IN PE1000_ADAPTER Adapter)
310 {
311     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
312 
313     if (Adapter->IoPort)
314     {
315         NdisMDeregisterIoPortRange(Adapter->AdapterHandle,
316                                    Adapter->IoPortAddress,
317                                    Adapter->IoPortLength,
318                                    Adapter->IoPort);
319     }
320 
321     if (Adapter->IoBase)
322     {
323         NdisMUnmapIoSpace(Adapter->AdapterHandle, Adapter->IoBase, Adapter->IoLength);
324     }
325 
326 
327     return NDIS_STATUS_SUCCESS;
328 }
329 
330 
331 NDIS_STATUS
332 NTAPI
333 NICPowerOn(
334     IN PE1000_ADAPTER Adapter)
335 {
336     NDIS_STATUS Status;
337     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
338 
339     Status = NICSoftReset(Adapter);
340     if (Status != NDIS_STATUS_SUCCESS)
341     {
342         return Status;
343     }
344 
345     if (!E1000ValidateNvmChecksum(Adapter))
346     {
347         return NDIS_STATUS_INVALID_DATA;
348     }
349 
350     return NDIS_STATUS_SUCCESS;
351 }
352 
353 NDIS_STATUS
354 NTAPI
355 NICSoftReset(
356     IN PE1000_ADAPTER Adapter)
357 {
358     ULONG Value, ResetAttempts;
359     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
360 
361     //em_get_hw_control(adapter);
362 
363     NICDisableInterrupts(Adapter);
364     E1000WriteUlong(Adapter, E1000_REG_RCTL, 0);
365     E1000ReadUlong(Adapter, E1000_REG_CTRL, &Value);
366     /* Write this using IO port, some devices cannot ack this otherwise */
367     E1000WriteIoUlong(Adapter, E1000_REG_CTRL, Value | E1000_CTRL_RST);
368 
369 
370     for (ResetAttempts = 0; ResetAttempts < MAX_RESET_ATTEMPTS; ResetAttempts++)
371     {
372         NdisStallExecution(100);
373         E1000ReadUlong(Adapter, E1000_REG_CTRL, &Value);
374 
375         if (!(Value & E1000_CTRL_RST))
376         {
377             NDIS_DbgPrint(MAX_TRACE, ("Device is back (%u)\n", ResetAttempts));
378 
379             NICDisableInterrupts(Adapter);
380             /* Clear out interrupts */
381             E1000ReadUlong(Adapter, E1000_REG_ICR, &Value);
382 
383             //NdisWriteRegisterUlong(Adapter->IoBase + E1000_REG_WUFC, 0);
384             //NdisWriteRegisterUlong(Adapter->IoBase + E1000_REG_VET, E1000_VET_VLAN);
385 
386             return NDIS_STATUS_SUCCESS;
387         }
388     }
389 
390     NDIS_DbgPrint(MIN_TRACE, ("Device did not recover\n"));
391     return NDIS_STATUS_FAILURE;
392 }
393 
394 NDIS_STATUS
395 NTAPI
396 NICEnableTxRx(
397     IN PE1000_ADAPTER Adapter)
398 {
399     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
400 
401     return NDIS_STATUS_SUCCESS;
402 }
403 
404 NDIS_STATUS
405 NTAPI
406 NICDisableTxRx(
407     IN PE1000_ADAPTER Adapter)
408 {
409     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
410 
411     return NDIS_STATUS_SUCCESS;
412 }
413 
414 NDIS_STATUS
415 NTAPI
416 NICGetPermanentMacAddress(
417     IN PE1000_ADAPTER Adapter,
418     OUT PUCHAR MacAddress)
419 {
420     USHORT AddrWord;
421     UINT n;
422 
423     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
424 
425     /* Should we read from RAL/RAH first? */
426     for (n = 0; n < (IEEE_802_ADDR_LENGTH / 2); ++n)
427     {
428         if (!E1000ReadEeprom(Adapter, (UCHAR)n, &AddrWord))
429             return NDIS_STATUS_FAILURE;
430         Adapter->PermanentMacAddress[n * 2 + 0] = AddrWord & 0xff;
431         Adapter->PermanentMacAddress[n * 2 + 1] = (AddrWord >> 8) & 0xff;
432     }
433 
434     NDIS_DbgPrint(MIN_TRACE, ("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
435                               Adapter->PermanentMacAddress[0],
436                               Adapter->PermanentMacAddress[1],
437                               Adapter->PermanentMacAddress[2],
438                               Adapter->PermanentMacAddress[3],
439                               Adapter->PermanentMacAddress[4],
440                               Adapter->PermanentMacAddress[5]));
441     return NDIS_STATUS_SUCCESS;
442 }
443 
444 NDIS_STATUS
445 NTAPI
446 NICUpdateMulticastList(
447     IN PE1000_ADAPTER Adapter)
448 {
449     UINT n;
450     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
451 
452     for (n = 0; n < MAXIMUM_MULTICAST_ADDRESSES; ++n)
453     {
454         ULONG Ral = *(ULONG *)Adapter->MulticastList[n].MacAddress;
455         ULONG Rah = *(USHORT *)&Adapter->MulticastList[n].MacAddress[4];
456 
457         if (Rah || Ral)
458         {
459             Rah |= E1000_RAH_AV;
460 
461             E1000WriteUlong(Adapter, E1000_REG_RAL + (8*n), Ral);
462             E1000WriteUlong(Adapter, E1000_REG_RAH + (8*n), Rah);
463         }
464         else
465         {
466             E1000WriteUlong(Adapter, E1000_REG_RAH + (8*n), 0);
467             E1000WriteUlong(Adapter, E1000_REG_RAL + (8*n), 0);
468         }
469     }
470 
471     return NDIS_STATUS_SUCCESS;
472 }
473 
474 NDIS_STATUS
475 NTAPI
476 NICApplyPacketFilter(
477     IN PE1000_ADAPTER Adapter)
478 {
479     ULONG FilterMask = 0;
480 
481     E1000ReadUlong(Adapter, E1000_REG_RCTL, &FilterMask);
482 
483     FilterMask &= ~E1000_RCTL_FILTER_BITS;
484 
485     if (Adapter->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST)
486     {
487         /* Multicast Promiscuous Enabled */
488         FilterMask |= E1000_RCTL_MPE;
489     }
490     if (Adapter->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
491     {
492         /* Unicast Promiscuous Enabled */
493         FilterMask |= E1000_RCTL_UPE;
494         /* Multicast Promiscuous Enabled */
495         FilterMask |= E1000_RCTL_MPE;
496     }
497     if (Adapter->PacketFilter & NDIS_PACKET_TYPE_MAC_FRAME)
498     {
499         /* Pass MAC Control Frames */
500         FilterMask |= E1000_RCTL_PMCF;
501     }
502     if (Adapter->PacketFilter & NDIS_PACKET_TYPE_BROADCAST)
503     {
504         /* Broadcast Accept Mode */
505         FilterMask |= E1000_RCTL_BAM;
506     }
507 
508     E1000WriteUlong(Adapter, E1000_REG_RCTL, FilterMask);
509 
510     return NDIS_STATUS_SUCCESS;
511 }
512 
513 NDIS_STATUS
514 NTAPI
515 NICApplyInterruptMask(
516     IN PE1000_ADAPTER Adapter)
517 {
518     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
519 
520     E1000WriteUlong(Adapter, E1000_REG_IMS, Adapter->InterruptMask);
521     return NDIS_STATUS_SUCCESS;
522 }
523 
524 NDIS_STATUS
525 NTAPI
526 NICDisableInterrupts(
527     IN PE1000_ADAPTER Adapter)
528 {
529     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
530 
531     E1000WriteUlong(Adapter, E1000_REG_IMC, ~0);
532     return NDIS_STATUS_SUCCESS;
533 }
534 
535 USHORT
536 NTAPI
537 NICInterruptRecognized(
538     IN PE1000_ADAPTER Adapter,
539     OUT PBOOLEAN InterruptRecognized)
540 {
541     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
542 
543     return 0;
544 }
545 
546 VOID
547 NTAPI
548 NICAcknowledgeInterrupts(
549     IN PE1000_ADAPTER Adapter)
550 {
551     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
552 }
553 
554 VOID
555 NTAPI
556 NICUpdateLinkStatus(
557     IN PE1000_ADAPTER Adapter)
558 {
559     ULONG SpeedIndex;
560     USHORT PhyStatus;
561     static ULONG SpeedValues[] = { 10, 100, 1000, 1000 };
562 
563     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
564 
565 #if 0
566     /* This does not work */
567     E1000ReadUlong(Adapter, E1000_REG_STATUS, &DeviceStatus);
568     E1000ReadUlong(Adapter, E1000_REG_STATUS, &DeviceStatus);
569     Adapter->MediaState = (DeviceStatus & E1000_STATUS_LU) ? NdisMediaStateConnected : NdisMediaStateDisconnected;
570     SpeedIndex = (DeviceStatus & E1000_STATUS_SPEEDMASK) >> E1000_STATUS_SPEEDSHIFT;
571     Adapter->LinkSpeedMbps = SpeedValues[SpeedIndex];
572 #else
573     /* Link bit can be sticky on some boards, read it twice */
574     if (!E1000ReadMdic(Adapter, E1000_PHY_STATUS, &PhyStatus))
575         NdisStallExecution(100);
576 
577     Adapter->MediaState = NdisMediaStateDisconnected;
578     Adapter->LinkSpeedMbps = 0;
579 
580     if (!E1000ReadMdic(Adapter, E1000_PHY_STATUS, &PhyStatus))
581         return;
582 
583     if (!(PhyStatus & E1000_PS_LINK_STATUS))
584         return;
585 
586     Adapter->MediaState = NdisMediaStateConnected;
587 
588     if (E1000ReadMdic(Adapter, E1000_PHY_SPECIFIC_STATUS, &PhyStatus))
589     {
590         if (PhyStatus & E1000_PSS_SPEED_AND_DUPLEX)
591         {
592             SpeedIndex = (PhyStatus & E1000_PSS_SPEEDMASK) >> E1000_PSS_SPEEDSHIFT;
593             Adapter->LinkSpeedMbps = SpeedValues[SpeedIndex];
594         }
595         else
596         {
597             NDIS_DbgPrint(MIN_TRACE, ("Speed and duplex not yet resolved, retry?.\n"));
598         }
599     }
600 #endif
601 }
602 
603 NDIS_STATUS
604 NTAPI
605 NICTransmitPacket(
606     IN PE1000_ADAPTER Adapter,
607     IN UCHAR TxDesc,
608     IN ULONG PhysicalAddress,
609     IN ULONG Length)
610 {
611     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
612 
613     return NDIS_STATUS_FAILURE;
614 }
615