reactos/drivers/network/dd/nvnet/phy.c

1145 lines
33 KiB
C
Raw Normal View History

/*
* 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 ********************************************************************/
BOOLEAN
MiiWrite(
_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;
}
BOOLEAN
MiiRead(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
PhyInitRealtek8211b(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
PhyInitRealtek8211c(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
PhyInitRealtek8201(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
PhyInitCicadaSemiconductor(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
PhyInitVitesseSemiconductor(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
PhyReset(
_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;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
PhyInit(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
FindPhyDevice(
_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;
}
static
CODE_SEG("PAGE")
BOOLEAN
SidebandUnitAcquireSemaphore(
_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;
}
VOID
SidebandUnitReleaseSemaphore(
_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);
}
}
static
CODE_SEG("PAGE")
BOOLEAN
SidebandUnitGetVersion(
_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;
}
static
BOOLEAN
MiiGetSpeedAndDuplex(
_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;
}
static
VOID
NvNetSetSpeedAndDuplex(
_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);
}
}
BOOLEAN
NvNetUpdateLinkSpeed(
_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_STATUS
NvNetPhyInit(
_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;
}