mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
1145 lines
33 KiB
C
1145 lines
33 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|