blob: 87e6db87cb704e25358941f96335db836998a1a2 [file] [log] [blame]
/*!
* \file eth_phy.c
* \brief Ethernet Physical Layer Interface Driver
* \version $Revision: 1.3 $
* \author Michael Norman
*
* This is a generic driver for all Ethernet PHYs with the basic MII registers
*/
#include "common.h"
#include "eth_phy.h"
#include "mii.h"
/* Variable to save off auto-negotiate settings */
int eth_phy_anar = 0
| PHY_ANAR_100BTX_FDX
| PHY_ANAR_100BTX
| PHY_ANAR_10BT_FDX
| PHY_ANAR_10BT;
int
eth_phy_reset(int ch, int phy_addr)
{
#if MII_CHECK_TIMEOUT
int timeout;
#endif
int settings;
/* Reset the PHY */
if (mii_write(ch, phy_addr, PHY_BMCR, PHY_BMCR_RESET))
return 1;
/* Wait for reset to complete */
#if MII_CHECK_TIMEOUT
for (timeout = 0; timeout < MII_LINK_TIMEOUT; ++timeout)
#endif
while(1)
{
/* Read back the contents of the CTRL register and verify
* that RESET is not set - this is a sanity check to ensure
* that we are talking to the PHY correctly. RESET should
* always be cleared. */
if (!(mii_read(ch, phy_addr, PHY_BMCR, &settings)) && !(settings & PHY_BMCR_RESET))
break;/*FSL: ready*/
}
#if MII_CHECK_TIMEOUT
if (timeout == MII_LINK_TIMEOUT || (settings & PHY_BMCR_RESET))
return 1;
else
#endif
return 0;
}
/********************************************************************/
/*!
* \brief Enable the Ethernet PHY in auto-negotiate mode
* \param phy_addr Address of the PHY
* \param speed Desired speed (MII_10BASE_T or MII_100BASE_TX)
* \param duplex Desired duplex (MII_FDX or MII_HDX)
* \return 0 if successful; non-zero otherwise
*/
int
eth_phy_autoneg(int ch, int phy_addr, ENET_SPEED speed, ENET_DUPLEX duplex)
{
int timeout, settings;
/* Reset the PHY */
eth_phy_reset(ch, phy_addr);
/* Set the Auto-Negotiation Advertisement Register */
if (speed == MII_10BASET)
{
settings = (duplex == MII_FDX)
? PHY_ANAR_10BT_FDX | PHY_ANAR_10BT
: PHY_ANAR_10BT;
}
else /* (speed == MII_100BASET) */
{
settings = (duplex == MII_FDX)
? PHY_ANAR_100BTX_FDX |
PHY_ANAR_100BTX |
PHY_ANAR_10BT_FDX |
PHY_ANAR_10BT
: PHY_ANAR_10BT_FDX |
PHY_ANAR_10BT;
}
/* Save off the settings we just advertised */
eth_phy_anar = settings;
if (mii_write(ch, phy_addr, PHY_ANAR, settings))
return 1;
/* Enable Auto-Negotiation */
if (mii_write(ch, phy_addr, PHY_BMCR, PHY_BMCR_AN_ENABLE | PHY_BMCR_AN_RESTART))
return 1;
/* Wait for auto-negotiation to complete */
for (timeout = 0; timeout < MII_LINK_TIMEOUT; ++timeout)
{
if (mii_read(ch, phy_addr, PHY_BMSR, &settings))
return 1;
if (settings & PHY_BMSR_AN_COMPLETE)
break;
}
/* Read the BMSR one last time */
if (mii_read(ch, phy_addr, PHY_BMSR, &settings))
return 1;
if (timeout == MII_LINK_TIMEOUT || !(settings & PHY_BMSR_LINK))
return 1;
else
return 0;
}
/********************************************************************/
/*!
* \brief Enable the Ethernet PHY in manual mode
* \param phy_addr Address of the PHY
* \param speed Desired speed (MII_10BASE_T or MII_100BASE_TX)
* \param duplex Desired duplex (MII_FDX or MII_HDX)
* \param loop Put PHY in loopback mode?
* \return 0 if successful; non-zero otherwise
*/
int
eth_phy_manual(int ch, int phy_addr, ENET_SPEED speed, ENET_DUPLEX duplex, int loop)
{
int timeout;
int settings = 0;
/* Reset the PHY */
/* Reset the PHY */
eth_phy_reset(ch, phy_addr);
if (loop)
settings |= PHY_BMCR_LOOP;
if (duplex == MII_FDX)
settings |= PHY_BMCR_FDX;
if (speed == MII_100BASET)
settings |= PHY_BMCR_SPEED;
if (mii_write(ch, phy_addr, PHY_BMCR, settings))
return 1;
/* Wait for link */
for (timeout = 0; timeout < MII_LINK_TIMEOUT; ++timeout)
{
if (mii_read(ch, phy_addr, PHY_BMSR, &settings))
return 1;
if (settings & PHY_BMSR_LINK)
break;
}
#if MII_CHECK_TIMEOUT
if (timeout == MII_LINK_TIMEOUT || !(settings & PHY_BMSR_LINK))
return 1;
else
#endif
return 0;
}
/********************************************************************/
/*!
* \brief Get the auto-negotiated speed
* \param phy_addr Address of the PHY
* \param speed Pointer where speed data is stored
* \return 0 if successful; non-zero otherwise
*/
int
eth_phy_get_speed(int ch, int phy_addr, int *speed)
{
#if MII_CHECK_TIMEOUT
int timeout;
#endif
int settings = 0;
/* Get Link Partner settings */
#if MII_CHECK_TIMEOUT
for (timeout = 0; timeout < MII_TIMEOUT; ++timeout)
#endif
while(1)
{
if (mii_read(ch, phy_addr, PHY_ANLPAR, &settings))
return 1;
else
break;
}
#if MII_CHECK_TIMEOUT
if (timeout == MII_TIMEOUT)
return 1;
#endif
settings &= eth_phy_anar;
if (settings & PHY_ANLPAR_100BT4 ||
settings & PHY_ANLPAR_100BTX_FDX ||
settings & PHY_ANLPAR_100BTX)
*speed = MII_100BASET;
else
*speed = MII_10BASET;
return 0;
}
/********************************************************************/
/*!
* \brief Get the auto-negotiated duplex
* \param phy_addr Address of the PHY
* \param speed Pointer where speed data is stored
* \return 0 if successful; non-zero otherwise
*/
int
eth_phy_get_duplex(int ch, int phy_addr, int *speed)
{
#if MII_CHECK_TIMEOUT
int timeout;
#endif
int settings = 0;
/* Get Link Partner settings */
#if MII_CHECK_TIMEOUT
for (timeout = 0; timeout < MII_TIMEOUT; ++timeout)
#endif
while(1)
{
if (mii_read(ch, phy_addr, PHY_ANLPAR, &settings))
return 1;
else
break;
}
#if MII_CHECK_TIMEOUT
if (timeout == MII_TIMEOUT)
return 1;
#endif
settings &= eth_phy_anar;
if (settings & PHY_ANLPAR_100BTX_FDX ||
settings & PHY_ANLPAR_10BTX_FDX)
*speed = MII_FDX;
else
*speed = MII_HDX;
return 0;
}
/********************************************************************/
/*!
* \brief Get the manual speed
* \param phy_addr Address of the PHY
* \param speed Pointer where speed data is stored
* \return 0 if successful; non-zero otherwise
*/
int
eth_phy_get_manual_speed(int ch, int phy_addr, int *speed)
{
#if MII_CHECK_TIMEOUT
int timeout;
#endif
int settings = 0;
/* Get Link Partner settings */
#if MII_CHECK_TIMEOUT
for (timeout = 0; timeout < MII_TIMEOUT; ++timeout)
#endif
while(1)
{
#ifdef TWR_K60N512
if (mii_read(ch, phy_addr, PHY_PHYCTRL2, &settings))//Micrel
#else
if (mii_read(ch, phy_addr, PHY_PHYSTS, &settings))//National Semiconductors
#endif
return 1;
else
break;
}
#if MII_CHECK_TIMEOUT
if (timeout == MII_TIMEOUT)
return 1;
#endif
#ifdef TWR_K60N512
/*FSL: obtain speed/duplex*/
settings = (settings & PHY_PHYCTRL2_OP_MOD_MASK)>>PHY_PHYCTRL2_OP_MOD_SHIFT;
if (settings == PHY_PHYCTRL2_MODE_OP_MOD_10MBPS_HD ||
settings == PHY_PHYCTRL2_MODE_OP_MOD_10MBPS_FD)
*speed = MII_10BASET;
else
*speed = MII_100BASET;
#else
if (settings & PHY_PHYSTS_SPEEDSTATUS)
*speed = MII_10BASET;
else
*speed = MII_100BASET;
#endif
return 0;
}
/********************************************************************/
/*!
* \brief Get the manual duplex
* \param phy_addr Address of the PHY
* \param duplex Pointer where duplex data is stored
* \return 0 if successful; non-zero otherwise
*/
int
eth_phy_get_manual_duplex(int ch, int phy_addr, int *duplex)
{
#if MII_CHECK_TIMEOUT
int timeout;
#endif
int settings = 0;
/* Get Link Partner settings */
#if MII_CHECK_TIMEOUT
for (timeout = 0; timeout < MII_TIMEOUT; ++timeout)
#endif
while(1)
{
#ifdef TWR_K60N512
if (mii_read(ch, phy_addr, PHY_PHYCTRL2, &settings))//Micrel
#else
if (mii_read(ch, phy_addr, PHY_PHYSTS, &settings))//National Semiconductors
#endif
return 1;
else
break;
}
#if MII_CHECK_TIMEOUT
if (timeout == MII_TIMEOUT)
return 1;
#endif
#ifdef TWR_K60N512
/*FSL: obtain speed/duplex*/
settings = (settings & PHY_PHYCTRL2_OP_MOD_MASK)>>PHY_PHYCTRL2_OP_MOD_SHIFT;
if (settings == PHY_PHYCTRL2_MODE_OP_MOD_10MBPS_HD ||
settings == PHY_PHYCTRL2_MODE_OP_MOD_100MBPS_HD)
*duplex = MII_HDX;
else
*duplex = MII_FDX;
#else
if (settings & PHY_PHYSTS_DUPLEXSTATUS)
*duplex = MII_FDX;
else
*duplex = MII_HDX;
#endif
return 0;
}
/********************************************************************/
/*!
* \brief Get the manual speed
* \param phy_addr Address of the PHY
* \param loop set if loopback is needed
* \return 0 if successful; non-zero otherwise
*/
int
eth_phy_set_remote_loopback(int ch, int phy_addr, int loop)
{
#if MII_CHECK_TIMEOUT
int timeout;
#endif
int settings = 0;
/* Get Link Partner settings */
#if MII_CHECK_TIMEOUT
for (timeout = 0; timeout < MII_TIMEOUT; ++timeout)
#endif
while(1)
{
if (mii_read(ch, phy_addr, PHY_PHYCTRL1, &settings))
return 1;
else
break;
}
#if MII_CHECK_TIMEOUT
if (timeout == MII_TIMEOUT)
return 1;
#endif
/*set remote loopback flag*/
if(loop)
settings |= PHY_PHYCTRL1_REMOTE_LOOP; /*set bit*/
else
settings &= ~PHY_PHYCTRL1_REMOTE_LOOP; /*clear bit*/
if (mii_write(ch, phy_addr, PHY_PHYCTRL1, settings))
return 1;
return 0;
}
/********************************************************************/
/*!
* \brief Print all the MII registers (0x00-0x1F)
* \param phy_addr Address of the PHY
*/
int
eth_phy_reg_dump(int ch, int phy_addr)
{
int j, settings;
for (j = 0; j < 32; j++)
{
mii_read(ch, phy_addr, j, &settings);
}
return 0;
}