You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
698 lines
20 KiB
698 lines
20 KiB
3 weeks ago
|
/* genericPhy.c - driver for generic 10/100/1000 ethernet PHY chips */
|
||
|
|
||
|
/*
|
||
|
* Copyright (c) 2005-2011, 2013-2017 Wind River Systems, Inc.
|
||
|
*
|
||
|
* The right to copy, distribute, modify or otherwise make use
|
||
|
* of this software may be licensed only pursuant to the terms
|
||
|
* of an applicable Wind River license agreement.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
modification history
|
||
|
--------------------
|
||
|
14mar17,myt fix link issue for 82574L and I210T (VXW6-86128)
|
||
|
27jun16,fao fix the genPhyInit routine. (VXW6-85481)
|
||
|
30oct15,wyt release pDrvCtl in genPhyInstUnlink(). (VXW6-84942)
|
||
|
24jul15,d_l add flow control advertise selection. (VXW6-84701)
|
||
|
fix genPhyProbe comment. (VXW6-84685)
|
||
|
16mar15,p_x Use MII_CR_RESTART instead of reset to restart
|
||
|
auto negotiation in genPhyModeSet(). (VXW6-84227)
|
||
|
02feb15,p_x Support turning off auto-negotiation. (VXW6-83965)
|
||
|
16mar14,xms add handle error status. (VXW6-28077)
|
||
|
20jan14,xms optimize the genPhyInit routine. (VXW6-38227)
|
||
|
01r,04sep13,xms fix NULL_RETURNS error. (WIND00414265)
|
||
|
01q,27feb13,y_y Add timeout check when phy reset. (WIND00404792)
|
||
|
01p,15mar11,x_z Remove workaround for MICREL KS8001 rev3
|
||
|
01o,14sep10,d_c Cleanup debug prints
|
||
|
01n,10sep10,d_c Add debug prints
|
||
|
01m,06aug10,x_z Add workaround for MICREL KS8001 rev3.
|
||
|
01l,09dec09,h_k increased VxBus version.
|
||
|
01k,05may08,tor update version
|
||
|
01k,21mar08,z_l Fix 1000M mode support error. (WIND00120712)
|
||
|
01j,20sep07,tor VXB_VERSION_3
|
||
|
01i,05jul07,wap Be sure to initialize BMCR register correctly in genPhyInit()
|
||
|
01h,25oct06,wap Detect gigE mode in modeSet routine when autoneg is disabled
|
||
|
01g,17oct06,pdg replaced VXB_METHOD_DRIVER_UNLINK with an address
|
||
|
01f,20jun06,wap Advertise manually set modes
|
||
|
01e,25may06,wap Add removal support
|
||
|
01d,31jan06,pdg updated vxbus version
|
||
|
01c,19jan06,wap Fix probe routine, use correct extended caps bit in status
|
||
|
register
|
||
|
01b,05jan06,wap Swap InstInit2 and InstConnect methods
|
||
|
01a,15sep05,wap written
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
DESCRIPTION
|
||
|
This module implements generic PHY access routines for 10/100/1000 copper
|
||
|
ethernet PHYs that comply with the 802.3u MII specification. Methods
|
||
|
are provided to initialize the PHY, set the media mode and check
|
||
|
the current media mode and link status. Ideally, this driver should
|
||
|
work with any 10/100 MII PHY. In practice, this isn't always feasible.
|
||
|
Some chips have quirks that require the use of custom code. Also, the
|
||
|
MII spec does not cover the use of interrupts. Not all PHYs are capable
|
||
|
of generating interrupts, but those that do must be configured through
|
||
|
vendor-specific registers, meaning that providing generic interrupt
|
||
|
methods is pretty much impossible.
|
||
|
|
||
|
The methods are accessed by calling the genPhyMethodsGet() routine,
|
||
|
which returns a pointer to a phyFuncs structure that contains
|
||
|
function pointers to the method routines. The functions can be invoked
|
||
|
through convenience macros declared in phyLib.h.
|
||
|
|
||
|
The initialization method resets always resets the PHY to a known
|
||
|
state, usually by toggling the power down bit in the control register
|
||
|
and then setting the reset bit. Then it programs the PHY for
|
||
|
autonegotiation. Chip-specific drivers may also do additional
|
||
|
setup or fixups if necessary.
|
||
|
|
||
|
The mode set routine can be used to program the PHY for a specific
|
||
|
media configuration. The mode word is based on the generic ifmedia
|
||
|
mode definitions, i.e. IFM_AUTO, IFM_100_TX, IFM_10_T, IFM_FDX and
|
||
|
IFM_HDX. Selecting a specific mode (other than auto) programs the
|
||
|
PHY for just that mode. This can be used to force the chip to run
|
||
|
at 10, 100Mbps or 1000Mbps and full or half duplex.
|
||
|
|
||
|
The mode get routine retrieves the current link speed and duplex
|
||
|
mode (i.e. what the link setting is right now) and the link state.
|
||
|
If the IFM_ACTIVE bit is not set in the link state, then the mode
|
||
|
information is set to IFM_NONE (no link available).
|
||
|
|
||
|
The interrupt control method can be used to turn interrupts for
|
||
|
link state change events on or off. Interrupts may be generated
|
||
|
for duplex, speed and link state changes.
|
||
|
|
||
|
The interrupt acknowledge method acks any pending interrupts so
|
||
|
that the chip will de-assert its interrupt pin.
|
||
|
|
||
|
By default, this driver doesn't advertise the flow control ability
|
||
|
when do auto-negotiation. But this can be overrided by adding a
|
||
|
parameter to the VXB_INST_PARAM_OVERRIDE table in hwconf.c
|
||
|
|
||
|
{ "genericPhy", 0, "fcmode", VXB_PARAM_INT32, {(void *)<fcmode>} }
|
||
|
|
||
|
The <fcmode> selection can be MII_FCADV_NONE, MII_FCADV_PAUSE,
|
||
|
MII_FCADV_ASM, MII_FCADV_PAUSE_ASM.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include <vxWorks.h>
|
||
|
#include <vxBusLib.h>
|
||
|
#include <logLib.h>
|
||
|
|
||
|
#include <hwif/vxbus/vxBus.h>
|
||
|
#include <hwif/util/hwMemLib.h>
|
||
|
|
||
|
#include <hwif/util/vxbParamSys.h>
|
||
|
#include "miiBus.h"
|
||
|
#include <../src/hwif/h/mii/genericPhy.h>
|
||
|
|
||
|
/* defines */
|
||
|
|
||
|
#undef GENERICPHY_DEBUG
|
||
|
#ifdef GENERICPHY_DEBUG
|
||
|
#define GENERICPHY_LOGMSG(fmt,p1,p2,p3,p4,p5,p6) logMsg(fmt,p1,p2,p3,p4,p5,p6)
|
||
|
#else
|
||
|
#define GENERICPHY_LOGMSG(fmt,p1,p2,p3,p4,p5,p6)
|
||
|
#endif
|
||
|
|
||
|
/* externs */
|
||
|
|
||
|
IMPORT BOOL autoNegForce;
|
||
|
|
||
|
/* locals */
|
||
|
|
||
|
LOCAL void genPhyInit (VXB_DEVICE_ID);
|
||
|
LOCAL STATUS genPhyModeSet (VXB_DEVICE_ID, UINT32);
|
||
|
LOCAL STATUS genPhyModeGet (VXB_DEVICE_ID, UINT32 *, UINT32 *);
|
||
|
LOCAL STATUS genPhyIntCtl (VXB_DEVICE_ID, int);
|
||
|
LOCAL STATUS genPhyIntAck (VXB_DEVICE_ID);
|
||
|
|
||
|
LOCAL void genPhyDevInstInit(VXB_DEVICE_ID pDev);
|
||
|
LOCAL void genPhyDevInstInit2(VXB_DEVICE_ID pDev);
|
||
|
LOCAL void genPhyDevInstConnect(VXB_DEVICE_ID pDev);
|
||
|
LOCAL BOOL genPhyProbe(VXB_DEVICE_ID pDev);
|
||
|
LOCAL STATUS genPhyInstUnlink (VXB_DEVICE_ID, void *);
|
||
|
|
||
|
LOCAL device_method_t genPhyMethods[] =
|
||
|
{
|
||
|
DEVMETHOD(miiModeGet, genPhyModeGet),
|
||
|
DEVMETHOD(miiModeSet, genPhyModeSet),
|
||
|
DEVMETHOD(vxbDrvUnlink, genPhyInstUnlink),
|
||
|
{ 0, 0 }
|
||
|
};
|
||
|
|
||
|
LOCAL struct drvBusFuncs genPhyFuncs =
|
||
|
{
|
||
|
genPhyDevInstInit, /* devInstanceInit */
|
||
|
genPhyDevInstInit2, /* devInstanceInit2 */
|
||
|
genPhyDevInstConnect /* devInstanceConnect */
|
||
|
};
|
||
|
|
||
|
LOCAL VXB_PARAMETERS genPhyParamDefaults[] =
|
||
|
{
|
||
|
{"fcmode", VXB_PARAM_INT32, {(void *)MII_FCADV_NONE}},
|
||
|
{NULL, VXB_PARAM_END_OF_LIST, {NULL}}
|
||
|
};
|
||
|
|
||
|
struct vxbDevRegInfo genPhyDevRegistration =
|
||
|
{
|
||
|
NULL, /* pNext */
|
||
|
VXB_DEVID_DEVICE, /* devID */
|
||
|
VXB_BUSID_MII, /* busID = MII Bus */
|
||
|
VXB_VER_5_0_0, /* busVer */
|
||
|
"genericPhy", /* drvName */
|
||
|
&genPhyFuncs, /* pDrvBusFuncs */
|
||
|
genPhyMethods, /* pMethods */
|
||
|
genPhyProbe, /* devProbe */
|
||
|
genPhyParamDefaults /* parameter defaults */
|
||
|
};
|
||
|
|
||
|
void genPhyRegister(void)
|
||
|
{
|
||
|
vxbDevRegister (&genPhyDevRegistration);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
LOCAL void genPhyDevInstInit
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev
|
||
|
)
|
||
|
{
|
||
|
vxbNextUnitGet (pDev);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
LOCAL void genPhyDevInstConnect
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev
|
||
|
)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*********************************************************************
|
||
|
*
|
||
|
* genPhyInstUnlink - VxBus unlink handler
|
||
|
*
|
||
|
* This function implements the VxBus unlink method for this driver.
|
||
|
* We delete each media type that was originally added to the MII bus
|
||
|
* instance by this device and take ourselves off the miiMonitor task
|
||
|
* list.
|
||
|
*
|
||
|
* RETURNS: OK if shutdown succeeds, else ERROR
|
||
|
*
|
||
|
* ERRNO: N/A
|
||
|
*/
|
||
|
|
||
|
LOCAL STATUS genPhyInstUnlink
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev,
|
||
|
void * unused
|
||
|
)
|
||
|
{
|
||
|
VXB_DEVICE_ID pBus;
|
||
|
MII_DRV_CTRL * pDrvCtrl;
|
||
|
UINT16 miiSts;
|
||
|
|
||
|
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
|
||
|
|
||
|
if (pDrvCtrl->miiInitialized == FALSE)
|
||
|
return (ERROR);
|
||
|
|
||
|
/* Only our parent bus can delete us. */
|
||
|
if (pDrvCtrl->miiLeaving == FALSE)
|
||
|
return (ERROR);
|
||
|
|
||
|
/* Remove ourselves from the miiMonitor task list. */
|
||
|
|
||
|
miiBusListDel (pDev);
|
||
|
|
||
|
/* Remove media list entries. */
|
||
|
|
||
|
if ((pBus = vxbDevParent (pDev)) == NULL)
|
||
|
return (ERROR);
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_STAT_REG, &miiSts);
|
||
|
|
||
|
if (miiSts & MII_SR_EXT_STS)
|
||
|
{
|
||
|
miiBusMediaDel (pBus, IFM_ETHER|IFM_1000_T);
|
||
|
miiBusMediaDel (pBus, IFM_ETHER|IFM_1000_T|IFM_FDX);
|
||
|
}
|
||
|
|
||
|
if (miiSts & MII_SR_TX_HALF_DPX)
|
||
|
miiBusMediaDel (pBus, IFM_ETHER|IFM_100_TX);
|
||
|
if (miiSts & MII_SR_TX_FULL_DPX)
|
||
|
miiBusMediaDel (pBus, IFM_ETHER|IFM_100_TX|IFM_FDX);
|
||
|
if (miiSts & MII_SR_10T_HALF_DPX)
|
||
|
miiBusMediaDel (pBus, IFM_ETHER|IFM_10_T);
|
||
|
if (miiSts & MII_SR_10T_FULL_DPX)
|
||
|
miiBusMediaDel (pBus, IFM_ETHER|IFM_10_T|IFM_FDX);
|
||
|
if (miiSts & MII_SR_AUTO_SEL)
|
||
|
miiBusMediaDel (pBus, IFM_ETHER|IFM_AUTO);
|
||
|
|
||
|
pDrvCtrl->miiInitialized = FALSE;
|
||
|
|
||
|
free (pDrvCtrl);
|
||
|
|
||
|
return (OK);
|
||
|
}
|
||
|
|
||
|
/*********************************************************************
|
||
|
*
|
||
|
* genPhyDevInstInit2 - vxBus instInit2 handler
|
||
|
*
|
||
|
* This routine does the final driver setup. The PHY registers
|
||
|
* its media types with its parent bus and adds itself to the MII
|
||
|
* monitoring list.
|
||
|
*
|
||
|
* RETURNS: N/A
|
||
|
*
|
||
|
* ERRNO: N/A
|
||
|
*/
|
||
|
|
||
|
LOCAL void genPhyDevInstInit2
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev
|
||
|
)
|
||
|
{
|
||
|
VXB_DEVICE_ID pBus;
|
||
|
MII_DRV_CTRL * pDrvCtrl;
|
||
|
VXB_INST_PARAM_VALUE val;
|
||
|
UINT16 miiSts;
|
||
|
|
||
|
GENERICPHY_LOGMSG("genPhyDevInstInit2(): entry for pDev: 0x%x\n",
|
||
|
(int)pDev, 0,0,0,0,0);
|
||
|
|
||
|
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
|
||
|
|
||
|
if (pDrvCtrl->miiInitialized == TRUE)
|
||
|
{
|
||
|
GENERICPHY_LOGMSG("genPhyDevInstInit2(): already initialized\n",
|
||
|
0,0,0,0,0,0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pDrvCtrl->miiInitialized = TRUE;
|
||
|
|
||
|
/*
|
||
|
* Tell miiBus about the media we support.
|
||
|
*/
|
||
|
|
||
|
if ((pBus = vxbDevParent (pDev)) == NULL)
|
||
|
return;
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_STAT_REG, &miiSts);
|
||
|
|
||
|
if (miiSts & MII_SR_EXT_STS)
|
||
|
{
|
||
|
miiBusMediaAdd (pBus, IFM_ETHER|IFM_1000_T);
|
||
|
miiBusMediaAdd (pBus, IFM_ETHER|IFM_1000_T|IFM_FDX);
|
||
|
}
|
||
|
|
||
|
if (miiSts & MII_SR_TX_HALF_DPX)
|
||
|
miiBusMediaAdd (pBus, IFM_ETHER|IFM_100_TX);
|
||
|
if (miiSts & MII_SR_TX_FULL_DPX)
|
||
|
miiBusMediaAdd (pBus, IFM_ETHER|IFM_100_TX|IFM_FDX);
|
||
|
if (miiSts & MII_SR_10T_HALF_DPX)
|
||
|
miiBusMediaAdd (pBus, IFM_ETHER|IFM_10_T);
|
||
|
if (miiSts & MII_SR_10T_FULL_DPX)
|
||
|
miiBusMediaAdd (pBus, IFM_ETHER|IFM_10_T|IFM_FDX);
|
||
|
if (miiSts & MII_SR_AUTO_SEL)
|
||
|
miiBusMediaAdd (pBus, IFM_ETHER|IFM_AUTO);
|
||
|
|
||
|
miiBusMediaDefaultSet (pBus, IFM_ETHER|IFM_AUTO);
|
||
|
|
||
|
/*
|
||
|
* Initialize the PHY. This may perform DSP code
|
||
|
* tweaking as needed.
|
||
|
*/
|
||
|
|
||
|
genPhyInit (pDev);
|
||
|
|
||
|
/* Add to the monitor list. */
|
||
|
|
||
|
miiBusListAdd (pDev);
|
||
|
|
||
|
/*
|
||
|
* paramDesc {
|
||
|
* The fcmode parameter specifies how we advertise
|
||
|
* the device flow control ability. The selection can be
|
||
|
* MII_FCADV_NONE, MII_FCADV_PAUSE, MII_FCADV_ASM,
|
||
|
* MII_FCADV_PAUSE_ASM. }
|
||
|
*/
|
||
|
|
||
|
if (vxbInstParamByNameGet (pDev, "fcmode", VXB_PARAM_INT32, &val) == OK)
|
||
|
{
|
||
|
pDrvCtrl->fcmode = (UINT16) (val.int32Val);
|
||
|
}
|
||
|
else
|
||
|
pDrvCtrl->fcmode = (UINT16) MII_FCADV_NONE;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*********************************************************************
|
||
|
*
|
||
|
* genPhyProbe - vxBus genericPhy probe handler
|
||
|
*
|
||
|
* This routine always returns TRUE.
|
||
|
*
|
||
|
* RETURNS: TRUE always.
|
||
|
*
|
||
|
* ERRNO: N/A
|
||
|
*/
|
||
|
|
||
|
LOCAL
|
||
|
BOOL genPhyProbe
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev
|
||
|
)
|
||
|
{
|
||
|
return (TRUE);
|
||
|
}
|
||
|
|
||
|
LOCAL void genPhyInit
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev
|
||
|
)
|
||
|
{
|
||
|
MII_DRV_CTRL * pDrvCtrl;
|
||
|
UINT16 miiSts;
|
||
|
UINT16 miiCtl;
|
||
|
UINT16 miiVal;
|
||
|
int i;
|
||
|
|
||
|
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
|
||
|
|
||
|
/* Get status register so we can look for extended capabilities. */
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_STAT_REG, &miiSts);
|
||
|
|
||
|
miiVal = MII_CR_POWER_DOWN;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, miiVal);
|
||
|
|
||
|
miiVal = 0;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, miiVal);
|
||
|
|
||
|
/* Set reset bit and then wait for it to clear. */
|
||
|
|
||
|
miiVal = MII_CR_RESET;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, miiVal);
|
||
|
|
||
|
for (i = 0; i < 1000; i++)
|
||
|
{
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, &miiCtl);
|
||
|
if (!(miiCtl & MII_CR_RESET))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (i == 1000)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If the extended capabilities bit is set, this is a gigE
|
||
|
* PHY, so make sure we advertise gigE modes.
|
||
|
*/
|
||
|
|
||
|
if (miiSts & MII_SR_EXT_STS)
|
||
|
{
|
||
|
/* Enable advertisement of gigE modes. */
|
||
|
miiVal = MII_MASSLA_CTRL_1000T_FD|MII_MASSLA_CTRL_1000T_HD;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_MASSLA_CTRL_REG, miiVal);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Some PHYs come out of reset with their isolate bit set. Make
|
||
|
* sure we don't write that bit back when setting the control
|
||
|
* register.
|
||
|
*/
|
||
|
|
||
|
miiCtl = MII_CR_AUTO_EN|MII_CR_RESTART;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, miiCtl);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
LOCAL STATUS genPhyModeSet
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev,
|
||
|
UINT32 mode
|
||
|
)
|
||
|
{
|
||
|
MII_DRV_CTRL * pDrvCtrl;
|
||
|
UINT16 miiVal;
|
||
|
UINT16 miiAnar = 0;
|
||
|
UINT16 gmiiAnar = 0;
|
||
|
UINT16 miiCtl = 0;
|
||
|
UINT16 miiSts;
|
||
|
BOOL autoneg = TRUE;
|
||
|
|
||
|
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
|
||
|
|
||
|
/* Get status register so we can look for extended capabilities. */
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_STAT_REG, &miiSts);
|
||
|
|
||
|
switch(IFM_SUBTYPE(mode)) {
|
||
|
case IFM_AUTO:
|
||
|
|
||
|
/* Set autoneg advertisement to advertise all modes. */
|
||
|
|
||
|
miiAnar = MII_ANAR_10TX_HD|MII_ANAR_10TX_FD|
|
||
|
MII_ANAR_100TX_HD|MII_ANAR_100TX_FD;
|
||
|
if (miiSts & MII_SR_EXT_STS)
|
||
|
gmiiAnar = MII_MASSLA_CTRL_1000T_FD|MII_MASSLA_CTRL_1000T_HD;
|
||
|
miiCtl = MII_CR_AUTO_EN|MII_CR_RESTART;
|
||
|
break;
|
||
|
case IFM_1000_T:
|
||
|
|
||
|
/* Auto-negotiation is mandatory per IEEE in 1000BASE-T. */
|
||
|
|
||
|
if (!(miiSts & MII_SR_EXT_STS))
|
||
|
return(ERROR);
|
||
|
if ((mode & IFM_GMASK) == IFM_FDX)
|
||
|
gmiiAnar = MII_MASSLA_CTRL_1000T_FD;
|
||
|
else
|
||
|
gmiiAnar = MII_MASSLA_CTRL_1000T_HD;
|
||
|
miiCtl = MII_CR_AUTO_EN|MII_CR_RESTART;
|
||
|
break;
|
||
|
case IFM_100_TX:
|
||
|
if (autoNegForce)
|
||
|
{
|
||
|
miiCtl = MII_CR_100|MII_CR_AUTO_EN|MII_CR_RESTART;
|
||
|
if ((mode & IFM_GMASK) == IFM_FDX)
|
||
|
{
|
||
|
miiAnar = MII_ANAR_100TX_FD;
|
||
|
miiCtl |= MII_CR_FDX;
|
||
|
}
|
||
|
else
|
||
|
miiAnar = MII_ANAR_100TX_HD;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
autoneg = FALSE;
|
||
|
|
||
|
/*
|
||
|
* Intel's PHY requires restarting auto-negotiation
|
||
|
* or software reset in order for speed/duplex changes
|
||
|
* to take effect. Use restarting auto-neg here.
|
||
|
*/
|
||
|
|
||
|
miiCtl = MII_CR_100|MII_CR_RESTART;
|
||
|
if ((mode & IFM_GMASK) == IFM_FDX)
|
||
|
miiCtl |= MII_CR_FDX;
|
||
|
}
|
||
|
break;
|
||
|
case IFM_10_T:
|
||
|
if (autoNegForce)
|
||
|
{
|
||
|
miiCtl = MII_CR_AUTO_EN|MII_CR_RESTART;
|
||
|
if ((mode & IFM_GMASK) == IFM_FDX)
|
||
|
{
|
||
|
miiAnar = MII_ANAR_10TX_FD;
|
||
|
miiCtl |= MII_CR_FDX;
|
||
|
}
|
||
|
else
|
||
|
miiAnar = MII_ANAR_10TX_HD;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
autoneg = FALSE;
|
||
|
miiCtl = MII_CR_RESTART;
|
||
|
if ((mode & IFM_GMASK) == IFM_FDX)
|
||
|
miiCtl |= MII_CR_FDX;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return (ERROR);
|
||
|
}
|
||
|
|
||
|
genPhyInit (pDev);
|
||
|
|
||
|
/* set flow control advertise ability */
|
||
|
|
||
|
miiAnar |= (pDrvCtrl->fcmode) << MII_ANAR_PAUSE_SHIFT;
|
||
|
|
||
|
if (autoneg)
|
||
|
{
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_AN_ADS_REG, &miiVal);
|
||
|
miiVal &= ~(MII_ANAR_10TX_HD|MII_ANAR_10TX_FD|
|
||
|
MII_ANAR_100TX_HD|MII_ANAR_100TX_FD);
|
||
|
miiVal |= miiAnar;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_AN_ADS_REG, miiVal);
|
||
|
|
||
|
if (miiSts & MII_SR_EXT_STS)
|
||
|
{
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_MASSLA_CTRL_REG, &miiVal);
|
||
|
miiVal &= ~(MII_MASSLA_CTRL_1000T_HD|MII_MASSLA_CTRL_1000T_FD);
|
||
|
miiVal |= gmiiAnar;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_MASSLA_CTRL_REG, miiVal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, &miiVal);
|
||
|
miiVal &= ~(MII_CR_FDX|MII_CR_100|MII_CR_1000|MII_CR_AUTO_EN|MII_CR_RESTART);
|
||
|
miiVal |= miiCtl;
|
||
|
miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, miiVal);
|
||
|
|
||
|
return(OK);
|
||
|
}
|
||
|
|
||
|
LOCAL STATUS genPhyModeGet
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev,
|
||
|
UINT32 * mode,
|
||
|
UINT32 * status
|
||
|
)
|
||
|
{
|
||
|
UINT16 miiSts;
|
||
|
UINT16 miiCtl;
|
||
|
UINT16 miiAnar;
|
||
|
UINT16 miiLpar;
|
||
|
UINT16 gmiiAnar = 0;
|
||
|
UINT16 gmiiLpar = 0;
|
||
|
UINT16 anlpar;
|
||
|
MII_DRV_CTRL * pDrvCtrl;
|
||
|
|
||
|
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
|
||
|
|
||
|
*mode = IFM_ETHER;
|
||
|
*status = IFM_AVALID;
|
||
|
|
||
|
GENERICPHY_LOGMSG("genPhyModeGet(): entry\n", 0,0,0,0,0,0);
|
||
|
|
||
|
/* read MII status register once to unlatch link status bit */
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_STAT_REG, &miiSts);
|
||
|
|
||
|
/* read again to know its current value */
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_STAT_REG, &miiSts);
|
||
|
|
||
|
/* no link bit means no carrier. */
|
||
|
if (!(miiSts & MII_SR_LINK_STATUS) || (miiSts == 0xFFFF))
|
||
|
{
|
||
|
*mode |= IFM_NONE;
|
||
|
GENERICPHY_LOGMSG("genPhyModeGet(): pDev: 0x%x no carrier\n",
|
||
|
(int)pDev,0,0,0,0,0);
|
||
|
return (OK);
|
||
|
}
|
||
|
|
||
|
*status |= IFM_ACTIVE;
|
||
|
|
||
|
/*
|
||
|
* read the control, ability advertisement and link
|
||
|
* partner advertisement registers.
|
||
|
*/
|
||
|
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_CTRL_REG, &miiCtl);
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_AN_ADS_REG, &miiAnar);
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_AN_PRTN_REG, &miiLpar);
|
||
|
|
||
|
if (miiSts & MII_SR_EXT_STS)
|
||
|
{
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_MASSLA_CTRL_REG, &gmiiAnar);
|
||
|
miiBusRead (pDev, pDrvCtrl->miiPhyAddr, MII_MASSLA_STAT_REG, &gmiiLpar);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If autoneg is on, figure out the link settings from the
|
||
|
* advertisement and partner ability registers. If autoneg is
|
||
|
* off, use the settings in the control register.
|
||
|
*/
|
||
|
|
||
|
if (miiCtl & MII_CR_AUTO_EN)
|
||
|
{
|
||
|
anlpar = miiAnar & miiLpar;
|
||
|
if ((gmiiAnar & MII_MASSLA_CTRL_1000T_FD) && \
|
||
|
(gmiiLpar & MII_MASSLA_STAT_LP1000T_FD))
|
||
|
*mode |= IFM_1000_T|IFM_FDX;
|
||
|
else if ((gmiiAnar & MII_MASSLA_CTRL_1000T_HD) && \
|
||
|
(gmiiLpar & MII_MASSLA_STAT_LP1000T_HD))
|
||
|
*mode |= IFM_1000_T|IFM_HDX;
|
||
|
else if (anlpar & MII_ANAR_100TX_FD)
|
||
|
*mode |= IFM_100_TX|IFM_FDX;
|
||
|
else if (anlpar & MII_ANAR_100TX_HD)
|
||
|
*mode |= IFM_100_TX|IFM_HDX;
|
||
|
else if (anlpar & MII_ANAR_10TX_FD)
|
||
|
*mode |= IFM_10_T|IFM_FDX;
|
||
|
else if (anlpar & MII_ANAR_10TX_HD)
|
||
|
*mode |= IFM_10_T|IFM_HDX;
|
||
|
else
|
||
|
*mode |= IFM_NONE;
|
||
|
|
||
|
GENERICPHY_LOGMSG("genPhyModeGet(): pDev: 0x%x auto-neg ON,"
|
||
|
" mode: 0x%x\n", (int)pDev,(int)*mode,0,0,0,0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (miiCtl & MII_CR_FDX)
|
||
|
*mode |= IFM_FDX;
|
||
|
else
|
||
|
*mode |= IFM_HDX;
|
||
|
if ((miiCtl & (MII_CR_100 | MII_CR_1000)) == (MII_CR_100 | MII_CR_1000))
|
||
|
*mode |= IFM_1000_T;
|
||
|
else if (miiCtl & MII_CR_100)
|
||
|
*mode |= IFM_100_TX;
|
||
|
else
|
||
|
*mode |= IFM_10_T;
|
||
|
|
||
|
GENERICPHY_LOGMSG("genPhyModeGet(): pDev: 0x%x auto-neg off,"
|
||
|
" mode: 0x%x\n",(int)pDev,(int)*mode,0,0,0,0);
|
||
|
}
|
||
|
|
||
|
return (OK);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Enable or disable PHY interrupts. If no interrupts supported,
|
||
|
* return ERROR.
|
||
|
*/
|
||
|
|
||
|
LOCAL STATUS genPhyIntCtl
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev,
|
||
|
int ctl
|
||
|
)
|
||
|
{
|
||
|
return (ERROR);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Ack PHY interrupts. Return OK if an interrupt was pending, error
|
||
|
* if not.
|
||
|
*/
|
||
|
|
||
|
LOCAL STATUS genPhyIntAck
|
||
|
(
|
||
|
VXB_DEVICE_ID pDev
|
||
|
)
|
||
|
{
|
||
|
return (ERROR);
|
||
|
}
|