Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145/* * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: PHY layer setup and management * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov <di.sean@protonmail.com> */
/* * HW access code was taken from the Linux forcedeth driver * Copyright (C) 2003,4,5 Manfred Spraul * Copyright (C) 2004 Andrew de Quincey * Copyright (C) 2004 Carl-Daniel Hailfinger * Copyright (c) 2004,2005,2006,2007,2008,2009 NVIDIA Corporation */
/* INCLUDES *******************************************************************/
#include "nvnet.h"
#define NDEBUG#include "debug.h"
/* GLOBALS ********************************************************************/
BOOLEANMiiWrite( _In_ PNVNET_ADAPTER Adapter, _In_ ULONG PhyAddress, _In_ ULONG RegAddress, _In_ ULONG Data){ ULONG i;
NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_RW);
if (NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE) { NV_WRITE(Adapter, NvRegMIIControl, NVREG_MIICTL_INUSE); NdisStallExecution(NV_MIIBUSY_DELAY); }
NV_WRITE(Adapter, NvRegMIIData, Data); NV_WRITE(Adapter, NvRegMIIControl, NVREG_MIICTL_WRITE | (PhyAddress << NVREG_MIICTL_ADDRSHIFT) | RegAddress);
for (i = NV_MIIPHY_DELAYMAX; i > 0; --i) { NdisStallExecution(NV_MIIPHY_DELAY);
if (!(NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE)) break; } if (i == 0) { return FALSE; }
return TRUE;}
BOOLEANMiiRead( _In_ PNVNET_ADAPTER Adapter, _In_ ULONG PhyAddress, _In_ ULONG RegAddress, _Out_ PULONG Data){ ULONG i;
NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_RW);
if (NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE) { NV_WRITE(Adapter, NvRegMIIControl, NVREG_MIICTL_INUSE); NdisStallExecution(NV_MIIBUSY_DELAY); }
NV_WRITE(Adapter, NvRegMIIControl, (PhyAddress << NVREG_MIICTL_ADDRSHIFT) | RegAddress);
for (i = NV_MIIPHY_DELAYMAX; i > 0; --i) { NdisStallExecution(NV_MIIPHY_DELAY);
if (!(NV_READ(Adapter, NvRegMIIControl) & NVREG_MIICTL_INUSE)) break; } if (i == 0) { *Data = 0; return FALSE; }
if (NV_READ(Adapter, NvRegMIIStatus) & NVREG_MIISTAT_ERROR) { *Data = 0; return FALSE; }
*Data = NV_READ(Adapter, NvRegMIIData); return TRUE;}
staticCODE_SEG("PAGE")BOOLEANPhyInitRealtek8211b( _In_ PNVNET_ADAPTER Adapter){ ULONG i; const struct { ULONG Register; ULONG Data; } Sequence[] = { { PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 }, { PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2 }, { PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3 }, { PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4 }, { PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5 }, { PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6 }, { PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 } };
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
for (i = 0; i < RTL_NUMBER_OF(Sequence); ++i) { if (!MiiWrite(Adapter, Adapter->PhyAddress, Sequence[i].Register, Sequence[i].Data)) return FALSE; }
return TRUE;}
staticCODE_SEG("PAGE")BOOLEANPhyInitRealtek8211c( _In_ PNVNET_ADAPTER Adapter){ ULONG PowerState, MiiRegister;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
PowerState = NV_READ(Adapter, NvRegPowerState2);
NV_WRITE(Adapter, NvRegPowerState2, PowerState | NVREG_POWERSTATE2_PHY_RESET); NdisMSleep(25000);
NV_WRITE(Adapter, NvRegPowerState2, PowerState); NdisMSleep(25000);
MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, &MiiRegister); MiiRegister |= PHY_REALTEK_INIT9; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, MiiRegister)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT10)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG7, &MiiRegister); if (!(MiiRegister & PHY_REALTEK_INIT11)) { MiiRegister |= PHY_REALTEK_INIT11; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG7, MiiRegister)) return FALSE; }
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) return FALSE;
return TRUE;}
staticCODE_SEG("PAGE")BOOLEANPhyInitRealtek8201( _In_ PNVNET_ADAPTER Adapter, _In_ BOOLEAN DisableCrossoverDetection){ ULONG MiiRegister;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
if (Adapter->Features & DEV_NEED_PHY_INIT_FIX) { MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, &MiiRegister); MiiRegister |= PHY_REALTEK_INIT7; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG6, MiiRegister)) return FALSE; }
if (DisableCrossoverDetection) { if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG2, &MiiRegister); MiiRegister &= ~PHY_REALTEK_INIT_MSK1; MiiRegister |= PHY_REALTEK_INIT3; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG2, MiiRegister)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) return FALSE; }
return TRUE;}
staticCODE_SEG("PAGE")BOOLEANPhyInitCicadaSemiconductor( _In_ PNVNET_ADAPTER Adapter, _In_ ULONG PhyInterface){ ULONG MiiRegister;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
if (PhyInterface & PHY_RGMII) { MiiRead(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG2, &MiiRegister); MiiRegister &= ~(PHY_CICADA_INIT1 | PHY_CICADA_INIT2); MiiRegister |= (PHY_CICADA_INIT3 | PHY_CICADA_INIT4); if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG2, MiiRegister)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG3, &MiiRegister); MiiRegister |= PHY_CICADA_INIT5; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG3, MiiRegister)) return FALSE; }
MiiRead(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG1, &MiiRegister); MiiRegister |= PHY_CICADA_INIT6; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_CICADA_INIT_REG1, MiiRegister)) return FALSE;
return TRUE;}
staticCODE_SEG("PAGE")BOOLEANPhyInitVitesseSemiconductor( _In_ PNVNET_ADAPTER Adapter){ ULONG MiiRegister;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT1)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT2)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, &MiiRegister); if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, MiiRegister)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, &MiiRegister); MiiRegister &= ~PHY_VITESSE_INIT_MSK1; MiiRegister |= PHY_VITESSE_INIT3; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, MiiRegister)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT4)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT5)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, &MiiRegister); MiiRegister &= ~PHY_VITESSE_INIT_MSK1; MiiRegister |= PHY_VITESSE_INIT3; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, MiiRegister)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, &MiiRegister); if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, MiiRegister)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT6)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT7)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, &MiiRegister); if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG4, MiiRegister)) return FALSE;
MiiRead(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, &MiiRegister); MiiRegister &= ~PHY_VITESSE_INIT_MSK2; MiiRegister |= PHY_VITESSE_INIT8; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG3, MiiRegister)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT9)) return FALSE;
if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT10)) return FALSE;
return TRUE;}
staticCODE_SEG("PAGE")BOOLEANPhyReset( _In_ PNVNET_ADAPTER Adapter, _In_ ULONG ControlSetup){ ULONG Tries = 0, MiiControl = MII_CR_RESET | ControlSetup;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl)) return FALSE;
NdisMSleep(500000);
do { NdisMSleep(10000);
MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl);
if (Tries++ > 100) return FALSE; } while (MiiControl & MII_CR_RESET);
return TRUE;}
staticCODE_SEG("PAGE")NDIS_STATUSPhyInit( _In_ PNVNET_ADAPTER Adapter){ ULONG PhyInterface, MiiRegister, MiiStatus, MiiControl;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
/* PHY errata for E3016 PHY */ if (Adapter->PhyModel == PHY_MODEL_MARVELL_E3016) { MiiRead(Adapter, Adapter->PhyAddress, PHY_MARVELL_INIT_REG1, &MiiRegister); MiiRegister &= ~PHY_MARVELL_E3016_INITMASK; if (!MiiWrite(Adapter, Adapter->PhyAddress, PHY_MARVELL_INIT_REG1, MiiRegister)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } }
if (Adapter->PhyOui == PHY_OUI_REALTEK) { if (Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && Adapter->PhyRevision == PHY_REV_REALTEK_8211B) { if (!PhyInitRealtek8211b(Adapter)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } else if (Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && Adapter->PhyRevision == PHY_REV_REALTEK_8211C) { if (!PhyInitRealtek8211c(Adapter)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } else if (Adapter->PhyModel == PHY_MODEL_REALTEK_8201) { if (!PhyInitRealtek8201(Adapter, FALSE)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } }
/* Set advertise register */ MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, &MiiRegister); if (Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX) { MiiRegister &= ~(MII_ADV_10T_HD | MII_ADV_10T_FD | MII_ADV_100T_HD | MII_ADV_100T_FD | MII_ADV_100T4 | MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM);
if (Adapter->Flags & NV_USER_SPEED_100) { if (Adapter->Flags & NV_FORCE_FULL_DUPLEX) MiiRegister |= MII_ADV_100T_FD; else MiiRegister |= MII_ADV_100T_HD; } else { if (Adapter->Flags & NV_FORCE_FULL_DUPLEX) MiiRegister |= MII_ADV_10T_FD; else MiiRegister |= MII_ADV_10T_HD; }
Adapter->PauseFlags &= ~(NV_PAUSEFRAME_AUTONEG | NV_PAUSEFRAME_RX_ENABLE | NV_PAUSEFRAME_TX_ENABLE); if (Adapter->PauseFlags & NV_PAUSEFRAME_RX_REQ) { /* For RX we set both advertisements but disable TX pause */ MiiRegister |= MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM; Adapter->PauseFlags |= NV_PAUSEFRAME_RX_ENABLE; } if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_REQ) { MiiRegister |= MII_ADV_PAUSE_ASYM; Adapter->PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; } } else { MiiRegister |= MII_ADV_10T_HD | MII_ADV_10T_FD | MII_ADV_100T_HD | MII_ADV_100T_FD | MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM; } if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiRegister)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed!\n")); return NDIS_STATUS_FAILURE; }
/* Get PHY interface type */ PhyInterface = NV_READ(Adapter, NvRegPhyInterface);
/* See if gigabit PHY */ MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); if (MiiStatus & PHY_GIGABIT) { ULONG MiiControl1000;
Adapter->Flags |= NV_GIGABIT_PHY;
MiiRead(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_CONTROL, &MiiControl1000); MiiControl1000 &= ~MII_MS_CR_1000T_HD; if ((PhyInterface & PHY_RGMII) && !(Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX)) MiiControl1000 |= MII_MS_CR_1000T_FD; else MiiControl1000 &= ~MII_MS_CR_1000T_FD; if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_CONTROL, MiiControl1000)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } else { Adapter->Flags &= ~NV_GIGABIT_PHY; }
MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl); MiiControl |= MII_CR_AUTONEG; if (Adapter->PhyOui == PHY_OUI_REALTEK && Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && Adapter->PhyRevision == PHY_REV_REALTEK_8211C) { /* Start auto-negation since we already performed HW reset above */ MiiControl |= MII_CR_AUTONEG_RESTART; if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } else { /* Reset the PHY (certain PHYs need BMCR to be setup with reset) */ if (!PhyReset(Adapter, MiiControl)) { NDIS_DbgPrint(MAX_TRACE, ("PHY reset failed\n")); return NDIS_STATUS_FAILURE; } }
/* PHY vendor specific configuration */ if (Adapter->PhyOui == PHY_OUI_CICADA) { if (!PhyInitCicadaSemiconductor(Adapter, PhyInterface)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } else if (Adapter->PhyOui == PHY_OUI_VITESSE) { if (!PhyInitVitesseSemiconductor(Adapter)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } else if (Adapter->PhyOui == PHY_OUI_REALTEK) { if (Adapter->PhyModel == PHY_MODEL_REALTEK_8211 && Adapter->PhyRevision == PHY_REV_REALTEK_8211B) { /* Reset could have cleared these out, set them back */ if (!PhyInitRealtek8211b(Adapter)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } else if (Adapter->PhyModel == PHY_MODEL_REALTEK_8201) { if (!PhyInitRealtek8201(Adapter, TRUE)) { NDIS_DbgPrint(MAX_TRACE, ("PHY init failed\n")); return NDIS_STATUS_FAILURE; } } }
/* Some PHYs clear out pause advertisement on reset, set it back */ MiiWrite(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiRegister);
/* Restart auto-negotiation */ MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl); MiiControl |= (MII_CR_AUTONEG_RESTART | MII_CR_AUTONEG); if (!MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl)) return NDIS_STATUS_FAILURE;
return NDIS_STATUS_SUCCESS;}
staticCODE_SEG("PAGE")BOOLEANFindPhyDevice( _Inout_ PNVNET_ADAPTER Adapter){ ULONG Phy;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
for (Phy = 1; Phy <= 32; ++Phy) { ULONG PhyAddress = Phy & 0x1F; /* Check the PHY 0 last */ ULONG PhyIdLow, PhyIdHigh;
if (!MiiRead(Adapter, PhyAddress, MII_PHY_ID1, &PhyIdLow)) continue; if (PhyIdLow == 0xFFFF) continue;
if (!MiiRead(Adapter, PhyAddress, MII_PHY_ID2, &PhyIdHigh)) continue; if (PhyIdHigh == 0xFFFF) continue;
Adapter->PhyAddress = PhyAddress; Adapter->PhyModel = PhyIdHigh & PHYID2_MODEL_MASK; Adapter->PhyOui = ((PhyIdLow & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT) | ((PhyIdHigh & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT);
/* Realtek hardcoded PhyIdLow to all zero's on certain PHYs */ if (Adapter->PhyOui == PHY_OUI_REALTEK2) Adapter->PhyOui = PHY_OUI_REALTEK;
/* Setup PHY revision for Realtek */ if (Adapter->PhyOui == PHY_OUI_REALTEK && Adapter->PhyModel == PHY_MODEL_REALTEK_8211) { ULONG PhyRevision;
MiiRead(Adapter, PhyAddress, PHY_REALTEK_REVISION, &PhyRevision); Adapter->PhyRevision = PhyRevision & PHY_REV_MASK; }
NDIS_DbgPrint(MIN_TRACE, ("Found PHY %X %X %X\n", Adapter->PhyAddress, Adapter->PhyModel, Adapter->PhyOui)); break; } if (Phy == 33) { return FALSE; }
return TRUE;}
staticCODE_SEG("PAGE")BOOLEANSidebandUnitAcquireSemaphore( _Inout_ PNVNET_ADAPTER Adapter){ ULONG i;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
for (i = 10; i > 0; --i) { if ((NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_SEMA_MASK) == NVREG_XMITCTL_MGMT_SEMA_FREE) { break; }
NdisMSleep(500000); } if (i == 0) { return FALSE; }
for (i = 0; i < 2; ++i) { ULONG TxControl = NV_READ(Adapter, NvRegTransmitterControl);
NV_WRITE(Adapter, NvRegTransmitterControl, TxControl | NVREG_XMITCTL_HOST_SEMA_ACQ);
/* Verify that the semaphore was acquired */ TxControl = NV_READ(Adapter, NvRegTransmitterControl); if (((TxControl & NVREG_XMITCTL_HOST_SEMA_MASK) == NVREG_XMITCTL_HOST_SEMA_ACQ) && ((TxControl & NVREG_XMITCTL_MGMT_SEMA_MASK) == NVREG_XMITCTL_MGMT_SEMA_FREE)) { Adapter->Flags |= NV_UNIT_SEMAPHORE_ACQUIRED; return TRUE; }
NdisStallExecution(50); }
return FALSE;}
VOIDSidebandUnitReleaseSemaphore( _In_ PNVNET_ADAPTER Adapter){ if (Adapter->Flags & NV_UNIT_SEMAPHORE_ACQUIRED) { ULONG TxControl;
TxControl = NV_READ(Adapter, NvRegTransmitterControl); TxControl &= ~NVREG_XMITCTL_HOST_SEMA_ACQ; NV_WRITE(Adapter, NvRegTransmitterControl, TxControl); }}
staticCODE_SEG("PAGE")BOOLEANSidebandUnitGetVersion( _In_ PNVNET_ADAPTER Adapter, _Out_ PULONG Version){ ULONG i, DataReady, DataReady2;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
DataReady = NV_READ(Adapter, NvRegTransmitterControl);
NV_WRITE(Adapter, NvRegMgmtUnitGetVersion, NVREG_MGMTUNITGETVERSION); NV_WRITE(Adapter, NvRegTransmitterControl, DataReady ^ NVREG_XMITCTL_DATA_START);
for (i = 100000; i > 0; --i) { DataReady2 = NV_READ(Adapter, NvRegTransmitterControl);
if ((DataReady & NVREG_XMITCTL_DATA_READY) != (DataReady2 & NVREG_XMITCTL_DATA_READY)) { break; }
NdisStallExecution(50); } if (i == 0 || DataReady2 & NVREG_XMITCTL_DATA_ERROR) { return FALSE; }
*Version = NV_READ(Adapter, NvRegMgmtUnitVersion) & NVREG_MGMTUNITVERSION;
return TRUE;}
staticBOOLEANMiiGetSpeedAndDuplex( _In_ PNVNET_ADAPTER Adapter, _Out_ PULONG MiiAdvertise, _Out_ PULONG MiiLinkPartnerAbility, _Out_ PULONG LinkSpeed, _Out_ PBOOLEAN FullDuplex){ ULONG MiiStatus, AdvLpa;
*MiiAdvertise = 0; *MiiLinkPartnerAbility = 0;
/* Link status is a latched-low bit, read it twice */ MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus);
/* Check link status */ if (!(MiiStatus & MII_SR_LINK_STATUS)) { /* No link detected - configure NIC for 10 MB HD */ *LinkSpeed = NVREG_LINKSPEED_10; *FullDuplex = FALSE; return FALSE; }
/* If we are forcing speed and duplex */ if (Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX) { if (Adapter->Flags & NV_USER_SPEED_100) { *LinkSpeed = NVREG_LINKSPEED_100; } else { *LinkSpeed = NVREG_LINKSPEED_10; } *FullDuplex = !!(Adapter->Flags & NV_FORCE_FULL_DUPLEX); return TRUE; }
/* Check auto-negotiation is complete */ if (!(MiiStatus & MII_SR_AUTONEG_COMPLETE)) { /* Still in auto-negotiation - configure NIC for 10 MBit HD and wait */ *LinkSpeed = NVREG_LINKSPEED_10; *FullDuplex = FALSE; return FALSE; }
MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiAdvertise); MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_LINK_PARTNER, MiiLinkPartnerAbility);
/* Gigabit ethernet */ if (Adapter->Flags & NV_GIGABIT_PHY) { ULONG MiiControl1000, MiiStatus1000;
MiiRead(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_CONTROL, &MiiControl1000); MiiRead(Adapter, Adapter->PhyAddress, MII_MASTER_SLAVE_STATUS, &MiiStatus1000);
if ((MiiControl1000 & MII_MS_CR_1000T_FD) && (MiiStatus1000 & MII_MS_SR_1000T_FD)) { *LinkSpeed = NVREG_LINKSPEED_1000; *FullDuplex = TRUE; return TRUE; } }
AdvLpa = (*MiiAdvertise) & (*MiiLinkPartnerAbility); if (AdvLpa & MII_LP_100T_FD) { *LinkSpeed = NVREG_LINKSPEED_100; *FullDuplex = TRUE; } else if (AdvLpa & MII_LP_100T_HD) { *LinkSpeed = NVREG_LINKSPEED_100; *FullDuplex = FALSE; } else if (AdvLpa & MII_LP_10T_FD) { *LinkSpeed = NVREG_LINKSPEED_10; *FullDuplex = TRUE; } else if (AdvLpa & MII_LP_10T_HD) { *LinkSpeed = NVREG_LINKSPEED_10; *FullDuplex = FALSE; } else { *LinkSpeed = NVREG_LINKSPEED_10; *FullDuplex = FALSE; }
return TRUE;}
staticVOIDNvNetSetSpeedAndDuplex( _In_ PNVNET_ADAPTER Adapter, _In_ ULONG MiiAdvertise, _In_ ULONG MiiLinkPartnerAbility){ ULONG PhyRegister, TxDeferral, PauseFlags, MiiExpansion; BOOLEAN RestartTransmitter = FALSE, RestartReceiver = FALSE;
/* The transmitter and receiver must be restarted for safe update */ if (NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_START) { RestartTransmitter = TRUE; NvNetStopTransmitter(Adapter); } if (NV_READ(Adapter, NvRegReceiverControl) & NVREG_RCVCTL_START) { RestartReceiver = TRUE; NvNetStopReceiver(Adapter); }
if (Adapter->Flags & NV_GIGABIT_PHY) { PhyRegister = NV_READ(Adapter, NvRegSlotTime); PhyRegister &= ~NVREG_SLOTTIME_1000_FULL; if ((Adapter->LinkSpeed == NVREG_LINKSPEED_10) || (Adapter->LinkSpeed == NVREG_LINKSPEED_100)) { PhyRegister |= NVREG_SLOTTIME_10_100_FULL; } else if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) { PhyRegister |= NVREG_SLOTTIME_1000_FULL; } NV_WRITE(Adapter, NvRegSlotTime, PhyRegister); }
PhyRegister = NV_READ(Adapter, NvRegPhyInterface); PhyRegister &= ~(PHY_HALF | PHY_100 | PHY_1000); if (!Adapter->FullDuplex) { PhyRegister |= PHY_HALF; } if (Adapter->LinkSpeed == NVREG_LINKSPEED_100) PhyRegister |= PHY_100; else if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) PhyRegister |= PHY_1000; NV_WRITE(Adapter, NvRegPhyInterface, PhyRegister);
/* Setup the deferral register */ MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_EXPANSION, &MiiExpansion); if (PhyRegister & PHY_RGMII) { if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) { TxDeferral = NVREG_TX_DEFERRAL_RGMII_1000; } else { if (!(MiiExpansion & MII_EXP_LP_AUTONEG) && !Adapter->FullDuplex && (Adapter->Features & DEV_HAS_COLLISION_FIX)) { TxDeferral = NVREG_TX_DEFERRAL_RGMII_STRETCH_10; } else { TxDeferral = NVREG_TX_DEFERRAL_RGMII_STRETCH_100; } } } else { if (!(MiiExpansion & MII_EXP_LP_AUTONEG) && !Adapter->FullDuplex && (Adapter->Features & DEV_HAS_COLLISION_FIX)) { TxDeferral = NVREG_TX_DEFERRAL_MII_STRETCH; } else { TxDeferral = NVREG_TX_DEFERRAL_DEFAULT; } } NV_WRITE(Adapter, NvRegTxDeferral, TxDeferral);
/* Setup the watermark register */ if (Adapter->Features & (DEV_HAS_HIGH_DMA | DEV_HAS_LARGEDESC)) { if (Adapter->LinkSpeed == NVREG_LINKSPEED_1000) NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC2_3_1000); else NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC2_3_DEFAULT); } else { NV_WRITE(Adapter, NvRegTxWatermark, NVREG_TX_WM_DESC1_DEFAULT); }
NV_WRITE(Adapter, NvRegMisc1, NVREG_MISC1_FORCE | (Adapter->FullDuplex ? 0 : NVREG_MISC1_HD)); NV_WRITE(Adapter, NvRegLinkSpeed, Adapter->LinkSpeed | NVREG_LINKSPEED_FORCE);
PauseFlags = 0;
/* Setup pause frames */ if (Adapter->FullDuplex) { if (!(Adapter->Flags & NV_FORCE_SPEED_AND_DUPLEX) && (Adapter->PauseFlags & NV_PAUSEFRAME_AUTONEG)) { ULONG AdvPause = MiiAdvertise & (MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM); ULONG LpaPause = MiiLinkPartnerAbility & (MII_LP_PAUSE_SYM | MII_LP_PAUSE_ASYM);
switch (AdvPause) { case MII_ADV_PAUSE_SYM: { if (LpaPause & MII_LP_PAUSE_SYM) { PauseFlags |= NV_PAUSEFRAME_RX_ENABLE;
if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_REQ) PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; } break; } case MII_ADV_PAUSE_ASYM: { if (LpaPause == (MII_LP_PAUSE_SYM | MII_LP_PAUSE_ASYM)) { PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; } break; } case (MII_ADV_PAUSE_SYM | MII_ADV_PAUSE_ASYM): { if (LpaPause & MII_LP_PAUSE_SYM) { PauseFlags |= NV_PAUSEFRAME_RX_ENABLE;
if (Adapter->PauseFlags & NV_PAUSEFRAME_TX_REQ) PauseFlags |= NV_PAUSEFRAME_TX_ENABLE; } if (LpaPause == MII_LP_PAUSE_ASYM) { PauseFlags |= NV_PAUSEFRAME_RX_ENABLE; } break; }
default: break; } } else { PauseFlags = Adapter->PauseFlags; } } NvNetUpdatePauseFrame(Adapter, PauseFlags);
if (RestartTransmitter) { NvNetStartTransmitter(Adapter); } if (RestartReceiver) { NvNetStartReceiver(Adapter); }}
BOOLEANNvNetUpdateLinkSpeed( _In_ PNVNET_ADAPTER Adapter){ ULONG MiiAdvertise, MiiLinkPartnerAbility, LinkSpeed; BOOLEAN FullDuplex, LinkUp;
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
LinkUp = MiiGetSpeedAndDuplex(Adapter, &MiiAdvertise, &MiiLinkPartnerAbility, &LinkSpeed, &FullDuplex); if (Adapter->FullDuplex == FullDuplex && Adapter->LinkSpeed == LinkSpeed) { return LinkUp; }
NDIS_DbgPrint(MIN_TRACE, ("Configuring MAC from '%lx %s-duplex' to '%lx %s-duplex'\n", Adapter->LinkSpeed, Adapter->FullDuplex ? "full" : "half", LinkSpeed, FullDuplex ? "full" : "half"));
Adapter->FullDuplex = FullDuplex; Adapter->LinkSpeed = LinkSpeed;
if (Adapter->Flags & NV_ACTIVE) { NdisDprAcquireSpinLock(&Adapter->Send.Lock); NdisDprAcquireSpinLock(&Adapter->Receive.Lock); }
NvNetSetSpeedAndDuplex(Adapter, MiiAdvertise, MiiLinkPartnerAbility);
if (Adapter->Flags & NV_ACTIVE) { NdisDprReleaseSpinLock(&Adapter->Receive.Lock); NdisDprReleaseSpinLock(&Adapter->Send.Lock); }
return LinkUp;}
CODE_SEG("PAGE")NDIS_STATUSNvNetPhyInit( _In_ PNVNET_ADAPTER Adapter){ ULONG PhyState; BOOLEAN RestorePhyState = FALSE, PhyInitialized = FALSE;
PAGED_CODE();
NDIS_DbgPrint(MIN_TRACE, ("()\n"));
/* Take PHY and NIC out of low power mode */ if (Adapter->Features & DEV_HAS_POWER_CNTRL) { ULONG PowerState = NV_READ(Adapter, NvRegPowerState2);
PowerState &= ~NVREG_POWERSTATE2_POWERUP_MASK; if ((Adapter->Features & DEV_NEED_LOW_POWER_FIX) && Adapter->RevisionId >= 0xA3) { PowerState |= NVREG_POWERSTATE2_POWERUP_REV_A3; } NV_WRITE(Adapter, NvRegPowerState2, PowerState); }
/* Clear PHY state and temporarily halt PHY interrupts */ NV_WRITE(Adapter, NvRegMIIMask, 0); PhyState = NV_READ(Adapter, NvRegAdapterControl); if (PhyState & NVREG_ADAPTCTL_RUNNING) { RestorePhyState = TRUE;
PhyState &= ~NVREG_ADAPTCTL_RUNNING; NV_WRITE(Adapter, NvRegAdapterControl, PhyState); } NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_MASK_ALL);
if (Adapter->Features & DEV_HAS_MGMT_UNIT) { ULONG UnitVersion;
/* Management unit running on the MAC? */ if ((NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST) && (NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) && SidebandUnitAcquireSemaphore(Adapter) && SidebandUnitGetVersion(Adapter, &UnitVersion)) { if (UnitVersion > 0) { if (NV_READ(Adapter, NvRegMgmtUnitControl) & NVREG_MGMTUNITCONTROL_INUSE) Adapter->Flags |= NV_MAC_IN_USE; else Adapter->Flags &= ~NV_MAC_IN_USE; } else { Adapter->Flags |= NV_MAC_IN_USE; }
NDIS_DbgPrint(MIN_TRACE, ("Management unit is running. MAC in use\n"));
/* Management unit setup the PHY already? */ if ((Adapter->Flags & NV_MAC_IN_USE) && ((NV_READ(Adapter, NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) == NVREG_XMITCTL_SYNC_PHY_INIT)) { /* PHY is inited by management unit */ PhyInitialized = TRUE;
NDIS_DbgPrint(MIN_TRACE, ("PHY already initialized by management unit\n")); } } }
/* Find a suitable PHY */ if (!FindPhyDevice(Adapter)) { NDIS_DbgPrint(MAX_TRACE, ("Could not find a valid PHY\n")); goto Failure; }
/* We need to init the PHY */ if (!PhyInitialized) { if (!PhyInit(Adapter)) { /* It's not critical for init, continue */ } } else { ULONG MiiStatus;
/* See if it is a gigabit PHY */ MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); if (MiiStatus & PHY_GIGABIT) { Adapter->Flags |= NV_GIGABIT_PHY; } }
return NDIS_STATUS_SUCCESS;
Failure: if (RestorePhyState) { NV_WRITE(Adapter, NvRegAdapterControl, PhyState | NVREG_ADAPTCTL_RUNNING); }
return NDIS_STATUS_FAILURE;}