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.

658 lines
17 KiB

/* vxbYt8521Phy.c - driver for yt8521 10/100/1000 ethernet PHY chips */
/*
*
* This program is OPEN SOURCE software: you can redistribute it and/or modify it;
* This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#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 <../src/hwif/h/mii/miiBus.h>
#include <vxbYt8521Phy.h>
/* defines */
#undef YT8521PHY_DEBUG
#ifdef YT8521PHY_DEBUG
#define YT8521PHY_LOGMSG(fmt,p1,p2,p3,p4,p5,p6) logMsg(fmt,p1,p2,p3,p4,p5,p6)
#else
#define YT8521PHY_LOGMSG(fmt,p1,p2,p3,p4,p5,p6)
#endif
/* externs */
IMPORT BOOL autoNegForce;
/* locals */
LOCAL STATUS ytPhyExtRead(VXB_DEVICE_ID ,UINT32 , UINT16*);
LOCAL STATUS ytPhyExtWrite(VXB_DEVICE_ID ,UINT32 ,UINT16);
LOCAL STATUS ytPhyConfigInit(VXB_DEVICE_ID pDev);
LOCAL void ytPhyInit (VXB_DEVICE_ID);
LOCAL STATUS ytPhyModeSet (VXB_DEVICE_ID, UINT32);
LOCAL STATUS ytPhyModeGet (VXB_DEVICE_ID, UINT32 *, UINT32 *);
LOCAL void ytPhyDevInstInit(VXB_DEVICE_ID pDev);
LOCAL void ytPhyDevInstInit2(VXB_DEVICE_ID pDev);
LOCAL void ytPhyDevInstConnect(VXB_DEVICE_ID pDev);
LOCAL BOOL ytPhyProbe(VXB_DEVICE_ID pDev);
LOCAL STATUS ytPhyInstUnlink (VXB_DEVICE_ID, void *);
LOCAL device_method_t ytPhyMethods[] =
{
DEVMETHOD(miiModeGet, ytPhyModeGet),
DEVMETHOD(miiModeSet, ytPhyModeSet),
DEVMETHOD(vxbDrvUnlink, ytPhyInstUnlink),
{ 0, 0 }
};
LOCAL struct drvBusFuncs ytPhyFuncs =
{
ytPhyDevInstInit, /* devInstanceInit */
ytPhyDevInstInit2, /* devInstanceInit2 */
ytPhyDevInstConnect /* devInstanceConnect */
};
LOCAL VXB_PARAMETERS ytPhyParamDefaults[] =
{
{"fcmode", VXB_PARAM_INT32, {(void *)MII_FCADV_NONE}},
{NULL, VXB_PARAM_END_OF_LIST, {NULL}}
};
struct vxbDevRegInfo ytPhyDevRegistration =
{
NULL, /* pNext */
VXB_DEVID_DEVICE, /* devID */
VXB_BUSID_MII, /* busID = MII Bus */
VXB_VER_5_0_0, /* busVer */
"yt8521Phy", /* drvName */
&ytPhyFuncs, /* pDrvBusFuncs */
ytPhyMethods, /* pMethods */
ytPhyProbe, /* devProbe */
ytPhyParamDefaults /* parameter defaults */
};
void ytPhyRegister(void)
{
vxbDevRegister (&ytPhyDevRegistration);
return;
}
LOCAL void ytPhyDevInstInit
(
VXB_DEVICE_ID pDev
)
{
vxbNextUnitGet (pDev);
return;
}
LOCAL void ytPhyDevInstConnect
(
VXB_DEVICE_ID pDev
)
{
return;
}
/*********************************************************************
*
* ytPhyInstUnlink - 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 ytPhyInstUnlink
(
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);
}
/*********************************************************************
*
* ytPhyDevInstInit2 - 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 ytPhyDevInstInit2
(
VXB_DEVICE_ID pDev
)
{
VXB_DEVICE_ID pBus;
MII_DRV_CTRL * pDrvCtrl;
VXB_INST_PARAM_VALUE val;
UINT16 miiSts;
YT8521PHY_LOGMSG("ytPhyDevInstInit2(): entry for pDev: 0x%x\n",
(int)pDev, 0,0,0,0,0);
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
if (pDrvCtrl->miiInitialized == TRUE)
{
YT8521PHY_LOGMSG("ytPhyDevInstInit2(): 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.
*/
ytPhyInit (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;
}
/*********************************************************************
*
* ytPhyProbe - vxBus yt8521Phy probe handler
*
* This routine always returns TRUE.
*
* RETURNS: TRUE always.
*
* ERRNO: N/A
*/
LOCAL BOOL ytPhyProbe
(
VXB_DEVICE_ID pDev
)
{
MII_DRV_CTRL * pDrvCtrl;
UINT16 miiId1;
UINT16 miiId2;
pDrvCtrl = pDev->pDrvCtrl;
miiId1 = pDrvCtrl->miiId1;
miiId2 = pDrvCtrl->miiId2;
if ((miiId1 == 0x0000)&&(miiId2==0x011a))
return (TRUE);
return (FALSE);
}
LOCAL void ytPhyInit
(
VXB_DEVICE_ID pDev
)
{
MII_DRV_CTRL * pDrvCtrl;
UINT16 miiSts;
UINT16 miiCtl;
UINT16 miiVal;
int i;
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
ytPhyConfigInit(pDev);
/* 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 ytPhyModeSet
(
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);
}
ytPhyInit (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 ytPhyModeGet
(
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;
YT8521PHY_LOGMSG("ytPhyModeGet(): 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;
YT8521PHY_LOGMSG("ytPhyModeGet(): 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;
YT8521PHY_LOGMSG("ytPhyModeGet(): 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;
YT8521PHY_LOGMSG("ytPhyModeGet(): pDev: 0x%x auto-neg off,"
" mode: 0x%x\n",(int)pDev,(int)*mode,0,0,0,0);
}
return (OK);
}
LOCAL STATUS ytPhyConfigInit
(
VXB_DEVICE_ID pDev
)
{
STATUS ret;
UINT16 miiVal;
/* disable auto sleep */
ret = ytPhyExtRead(pDev,YT8521_EXTREG_SLEEP_CONTROL1,&miiVal);
if (ret < 0)
return ret;
miiVal &= ~(1 << YT8521_EN_SLEEP_SW_BIT);
ret = ytPhyExtWrite(pDev,YT8521_EXTREG_SLEEP_CONTROL1,miiVal);
if (ret < 0)
return ret;
/* enable RXC clock when no wire plug */
ret = ytPhyExtWrite(pDev,YT8521_EXTREG_SMI_SDS_PHY,0);
if (ret < 0)
return ret;
ret = ytPhyExtRead(pDev,YT8521_EXTREG_C,&miiVal);
if (ret < 0)
return ret;
miiVal &= ~(1 << 12);
ret = ytPhyExtWrite(pDev,YT8521_EXTREG_C,miiVal);
return ret;
}
LOCAL STATUS ytPhyExtRead
(
VXB_DEVICE_ID pDev,
UINT32 reg,
UINT16* val
)
{
STATUS ret;
MII_DRV_CTRL * pDrvCtrl;
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
ret = miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, REG_DEBUG_ADDR_OFFSET, reg);
if (ret < 0)
return ret;
ret = miiBusRead (pDev, pDrvCtrl->miiPhyAddr, REG_DEBUG_DATA, val);
return ret;
}
LOCAL STATUS ytPhyExtWrite
(
VXB_DEVICE_ID pDev,
UINT32 reg,
UINT16 val
)
{
STATUS ret;
MII_DRV_CTRL * pDrvCtrl;
pDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl;
ret = miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, REG_DEBUG_ADDR_OFFSET, reg);
if (ret < 0)
return ret;
ret = miiBusWrite (pDev, pDrvCtrl->miiPhyAddr, REG_DEBUG_DATA, val);
return (ret);
}