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

/* 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);
}