Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources
This commit is contained in:
580
sdk/lib/net/ethphy/eth_phy_device.c
Normal file
580
sdk/lib/net/ethphy/eth_phy_device.c
Normal file
@@ -0,0 +1,580 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file sdk/lib/ethernet_phy/eth_phy_device.c
|
||||
* @author HUGE-IC Application Team
|
||||
* @version V1.0.0
|
||||
* @date 10-10-2018
|
||||
* @brief Framework for finding and configuring PHYs.
|
||||
* Also contains generic PHY driver
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT 2018 HUGE-IC</center></h2>
|
||||
*
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "typesdef.h"
|
||||
#include "errno.h"
|
||||
#include "list.h"
|
||||
#include "dev.h"
|
||||
#include "osal/task.h"
|
||||
#include "osal/string.h"
|
||||
#include "hal/netdev.h"
|
||||
|
||||
#include "lib/net/ethphy/eth_mdio_bus.h"
|
||||
#include "lib/net/ethphy/eth_phy.h"
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
|
||||
/** @addtogroup NETWORK_PHY
|
||||
* @{
|
||||
*/
|
||||
|
||||
struct ethernet_phy_driver genphy_driver = {
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.config_init = genphy_config_init,
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convenience function for reading a given PHY register
|
||||
* @param phydev : the phy_device struct
|
||||
* @param reg : register number to read
|
||||
* @retval Returns the data in the PHY register
|
||||
*/
|
||||
uint16 phy_read(struct ethernet_phy_device *phydev, uint16 reg) {
|
||||
return phydev->bus->read(phydev->bus->priv, phydev->addr, reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convenience function for writing a given PHY register
|
||||
* @param phydev : the phy_device struct
|
||||
* @param reg : register number to write
|
||||
* @param val : value to write to @reg
|
||||
* @retval None
|
||||
*/
|
||||
void phy_write(struct ethernet_phy_device *phydev, uint16 reg, uint16 val) {
|
||||
phydev->bus->write(phydev->bus->priv, phydev->addr, reg, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A small helper function that translates ethtool advertisement
|
||||
* settings to phy autonegotiation advertisements for the
|
||||
* MII_ADVERTISE register.
|
||||
* @param ethadv: the ethtool advertisement settings
|
||||
* @retval MII_ADVERTISE register value
|
||||
*/
|
||||
static inline uint32 eth_phy_adv_to_mii_adv_t(uint32 ethadv) {
|
||||
uint32 result = 0;
|
||||
|
||||
if(ethadv & ADVERTISED_10baseT_Half) {
|
||||
result |= ADVERTISE_10HALF;
|
||||
}
|
||||
if(ethadv & ADVERTISED_10baseT_Full) {
|
||||
result |= ADVERTISE_10FULL;
|
||||
}
|
||||
if(ethadv & ADVERTISED_100baseT_Half) {
|
||||
result |= ADVERTISE_100HALF;
|
||||
}
|
||||
if(ethadv & ADVERTISED_100baseT_Full) {
|
||||
result |= ADVERTISE_100FULL;
|
||||
}
|
||||
if(ethadv & ADVERTISED_Pause) {
|
||||
result |= ADVERTISE_PAUSE_CAP;
|
||||
}
|
||||
if(ethadv & ADVERTISED_Asym_Pause) {
|
||||
result |= ADVERTISE_PAUSE_ASYM;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the phy_device structure.
|
||||
* @param dev : the phy_device struct
|
||||
* @retval None
|
||||
*/
|
||||
__init void eth_phy_device_create(struct ethernet_phy_device *dev)
|
||||
{
|
||||
dev->speed = 0;
|
||||
dev->duplex = -1;
|
||||
dev->pause = dev->asym_pause = 0;
|
||||
dev->link = 1;
|
||||
|
||||
dev->autoneg = AUTONEG_ENABLE;
|
||||
dev->state = ETH_PHY_DOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reads the specified addr for its ID.
|
||||
* @param bus : the target MII bus
|
||||
* @retval Returning 0 means success and returning other values means
|
||||
* failure.
|
||||
* @note In the case of a 802.3-c22 PHY, reads the ID registers of the
|
||||
* PHY at @addr on the @bus, stores it in @phy_id and returns zero
|
||||
* on success.
|
||||
*/
|
||||
__init static int eth_get_phy_id(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int phy_reg;
|
||||
|
||||
/* Grab the bits from PHYIR1, and put them
|
||||
* in the upper half */
|
||||
phy_reg = phy_read(phydev, MII_PHYSID1);
|
||||
|
||||
if(phy_reg < 0) {
|
||||
return -1;
|
||||
}
|
||||
phydev->phy_id = (phy_reg & 0xffff) << 16;
|
||||
|
||||
/* Grab the bits from PHYIR2, and put them in the lower half */
|
||||
phy_reg = phy_read(phydev, MII_PHYSID2);
|
||||
|
||||
if(phy_reg < 0) {
|
||||
return -1;
|
||||
}
|
||||
phydev->phy_id |= (phy_reg & 0xffff);
|
||||
|
||||
//unvaild ID
|
||||
switch(phydev->phy_id) {
|
||||
case 0xFFFFFFFF:
|
||||
case 0x00000000:
|
||||
case 0x0000FFFF:
|
||||
case 0xFFFF0000:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reads the specified PHY device
|
||||
* @param phydev : the phy_device struct
|
||||
* @retval Return 1 to successfully initialize phy_device.
|
||||
*/
|
||||
__init uint8 eth_get_phy_device(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
uint32 i;
|
||||
|
||||
//判断是否需要自适应查找地址
|
||||
if(phydev->addr == PHY_ADAPTIVE_ADDR) {
|
||||
for(i=1; i<=PHY_MAX_ADDR; i++) {
|
||||
phydev->addr = i;
|
||||
if(!eth_get_phy_id(phydev)) {
|
||||
/* If the phy_id is mostly Fs, there is no device there */
|
||||
if((phydev->phy_id & 0x1fffffff) != 0x1fffffff) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//not found
|
||||
if(i > PHY_MAX_ADDR) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if(eth_get_phy_id(phydev)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the phy_id is mostly Fs, there is no device there */
|
||||
if((phydev->phy_id & 0x1fffffff) == 0x1fffffff) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
os_printf("eth phy id:%x\r\n", phydev->phy_id);
|
||||
|
||||
eth_phy_device_create(phydev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__init int genphy_config_init(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int val;
|
||||
uint32 features;
|
||||
|
||||
/* For now, I'll claim that the generic driver supports
|
||||
* all possible port types */
|
||||
features = (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
|
||||
SUPPORTED_FIBRE | SUPPORTED_BNC);
|
||||
|
||||
/* Do we support autonegotiation? */
|
||||
val = phy_read(phydev, MII_BMSR);
|
||||
|
||||
if(val < 0) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if(val & BMSR_ANEGCAPABLE) {
|
||||
features |= SUPPORTED_Autoneg;
|
||||
}
|
||||
|
||||
if(val & BMSR_100FULL) {
|
||||
features |= SUPPORTED_100baseT_Full;
|
||||
}
|
||||
if(val & BMSR_100HALF) {
|
||||
features |= SUPPORTED_100baseT_Half;
|
||||
}
|
||||
if(val & BMSR_10FULL) {
|
||||
features |= SUPPORTED_10baseT_Full;
|
||||
}
|
||||
if(val & BMSR_10HALF) {
|
||||
features |= SUPPORTED_10baseT_Half;
|
||||
}
|
||||
if(val & BMSR_ESTATEN) {
|
||||
val = phy_read(phydev, MII_ESTATUS);
|
||||
|
||||
if(val < 0) {
|
||||
return val;
|
||||
}
|
||||
if(val & ESTATUS_1000_TFULL) {
|
||||
features |= SUPPORTED_1000baseT_Full;
|
||||
}
|
||||
if(val & ESTATUS_1000_THALF) {
|
||||
features |= SUPPORTED_1000baseT_Half;
|
||||
}
|
||||
}
|
||||
|
||||
phydev->supported = features;
|
||||
phydev->advertising = features;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief attach a network device to a given PHY device pointer
|
||||
* @param priv : PHY private structure pointer.
|
||||
* @param phydev : Pointer to phy_device to attach
|
||||
* @retval always return 1
|
||||
* @note Called by drivers to attach to a particular PHY device. The
|
||||
* phy_device is found, and properly hooked up to the phy_driver.
|
||||
* If no driver is attached, then the genphy_driver is used. The
|
||||
* phy_device is given a ptr to the attaching device, and given a
|
||||
* callback for link status change. The phy_device is returned to
|
||||
* the attaching driver.
|
||||
*/
|
||||
__init static int32 eth_phy_attach_direct(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
phydev->state = ETH_PHY_READY;
|
||||
|
||||
/* Do initial configuration here, now that
|
||||
* we have certain key parameters
|
||||
* (dev_flags and interface) */
|
||||
return phydev->drv->config_init(phydev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief connect an ethernet device to a specific phy_device
|
||||
* @param phydev : the pointer to the phy device
|
||||
* @param handler : callback function for state change notifications
|
||||
* @retval Returns RET_OK if the PHY connection is successful
|
||||
*/
|
||||
__init int32 eth_phy_connect_direct(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int32 flag;
|
||||
|
||||
flag = eth_phy_attach_direct(phydev);
|
||||
if(flag == RET_ERR) {
|
||||
return RET_ERR;
|
||||
}
|
||||
|
||||
eth_phy_start_machine(phydev);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sanitize and advertise auto-negotiation parameters
|
||||
* @param phydev : target phy_device struct
|
||||
* @retval Returns < 0 on error, 0 if the PHY's advertisement hasn't
|
||||
* changed, and > 0 if it has changed.
|
||||
* @note Writes MII_ADVERTISE with the appropriate values, after
|
||||
* sanitizing the values to make sure we only advertise what is
|
||||
* supported.
|
||||
*/
|
||||
static int genphy_config_advert(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
uint32 advertise;
|
||||
int oldadv, adv;
|
||||
int changed = 0;
|
||||
|
||||
/* Only allow advertising what
|
||||
* this PHY supports */
|
||||
phydev->advertising &= phydev->supported;
|
||||
advertise = phydev->advertising;
|
||||
|
||||
/* Setup standard advertisement */
|
||||
oldadv = adv = phy_read(phydev, MII_ADVERTISE);
|
||||
|
||||
if(adv < 0) {
|
||||
return adv;
|
||||
}
|
||||
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
|
||||
ADVERTISE_PAUSE_ASYM);
|
||||
adv |= eth_phy_adv_to_mii_adv_t(advertise);
|
||||
|
||||
if(adv != oldadv) {
|
||||
phy_write(phydev, MII_ADVERTISE, adv);
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
/* Configure gigabit if it's supported */
|
||||
// if (phydev->supported & (SUPPORTED_1000baseT_Half |
|
||||
// SUPPORTED_1000baseT_Full)) {
|
||||
// oldadv = adv = phy_read(phydev, MII_CTRL1000);
|
||||
|
||||
// if (adv < 0)
|
||||
// return adv;
|
||||
|
||||
// adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
|
||||
// adv |= ethtool_adv_to_mii_ctrl1000_t(advertise);
|
||||
|
||||
// if (adv != oldadv) {
|
||||
// err = phy_write(phydev, MII_CTRL1000, adv);
|
||||
|
||||
// if (err < 0)
|
||||
// return err;
|
||||
// changed = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief configures/forces speed/duplex from @phydev
|
||||
* @param phydev : target phy_device struct
|
||||
* @retval None
|
||||
* @note Configures MII_BMCR to force speed/duplex to the values in
|
||||
* phydev. Assumes that the values are valid. Please see
|
||||
* eth_phy_sanitize_settings().
|
||||
*/
|
||||
static void genphy_setup_forced(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int ctl = 0;
|
||||
|
||||
phydev->pause = phydev->asym_pause = 0;
|
||||
|
||||
if(SPEED_1000 == phydev->speed) {
|
||||
ctl |= BMCR_SPEED1000;
|
||||
} else if(SPEED_100 == phydev->speed) {
|
||||
ctl |= BMCR_SPEED100;
|
||||
}
|
||||
if(DUPLEX_FULL == phydev->duplex) {
|
||||
ctl |= BMCR_FULLDPLX;
|
||||
}
|
||||
phy_write(phydev, MII_BMCR, ctl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable and Restart Autonegotiation
|
||||
* @param phydev : target phy_device struct
|
||||
* @retval Returning 0 means the PHY supports auto-negotiation.
|
||||
*/
|
||||
int genphy_restart_aneg(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int ctl;
|
||||
|
||||
ctl = phy_read(phydev, MII_BMCR);
|
||||
|
||||
if(ctl < 0) {
|
||||
return ctl;
|
||||
}
|
||||
ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
|
||||
|
||||
/* Don't isolate the PHY if we're negotiating */
|
||||
ctl &= ~(BMCR_ISOLATE);
|
||||
|
||||
phy_write(phydev, MII_BMCR, ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief restart auto-negotiation or write BMCR
|
||||
* @param phydev : target phy_device struct
|
||||
* @retval Returns the auto-negotiation configuration result.
|
||||
* @note If auto-negotiation is enabled, we configure the advertising,
|
||||
* and then restart auto-negotiation. If it is not enabled, then we
|
||||
* write the BMCR
|
||||
*/
|
||||
int genphy_config_aneg(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int result;
|
||||
|
||||
if(AUTONEG_ENABLE != phydev->autoneg) {
|
||||
genphy_setup_forced(phydev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = genphy_config_advert(phydev);
|
||||
|
||||
if(result < 0) { /* error */
|
||||
return result;
|
||||
}
|
||||
|
||||
if(result == 0) {
|
||||
/* Advertisement hasn't changed, but maybe aneg was never on to
|
||||
* begin with? Or maybe phy was isolated? */
|
||||
int ctl = phy_read(phydev, MII_BMCR);
|
||||
|
||||
if(ctl < 0) {
|
||||
return ctl;
|
||||
}
|
||||
if(!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) {
|
||||
result = 1; /* do restart aneg */
|
||||
}
|
||||
}
|
||||
|
||||
/* Only restart aneg if we are advertising something different
|
||||
* than we were before. */
|
||||
if(result > 0) {
|
||||
result = genphy_restart_aneg(phydev);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief update link status in @phydev
|
||||
* @param phydev : target phy_device struct
|
||||
* @retval Returns the link status of the PHY.
|
||||
* @note Update the value in phydev->link to reflect the current link
|
||||
* value. In order to do this, we need to read the status register
|
||||
* twice, keeping the second value.
|
||||
*/
|
||||
int genphy_update_link(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Do a fake read */
|
||||
status = phy_read(phydev, MII_BMSR);
|
||||
|
||||
if(status < 0) {
|
||||
return status;
|
||||
}
|
||||
/* Read link and autonegotiation status */
|
||||
status = phy_read(phydev, MII_BMSR);
|
||||
|
||||
if(status < 0) {
|
||||
return status;
|
||||
}
|
||||
if((status & BMSR_LSTATUS) == 0) {
|
||||
phydev->link = 0;
|
||||
} else {
|
||||
phydev->link = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check the link status and update current link state
|
||||
* @param phydev : target phy_device struct
|
||||
* @retval Returns 0 when the PHY has no errors.
|
||||
* @note Check the link, then figure out the current state by comparing
|
||||
* what we advertise with what the link partner advertises. Start
|
||||
* by checking the gigabit possibilities, then move on to 10/100.
|
||||
*/
|
||||
int genphy_read_status(struct ethernet_phy_device *phydev)
|
||||
{
|
||||
int adv;
|
||||
int err;
|
||||
int lpa;
|
||||
int lpagb = 0;
|
||||
|
||||
/* Update the link, but return if there
|
||||
* was an error */
|
||||
err = genphy_update_link(phydev);
|
||||
if(err) {
|
||||
return err;
|
||||
}
|
||||
if(AUTONEG_ENABLE == phydev->autoneg) {
|
||||
if(phydev->supported & (SUPPORTED_1000baseT_Half |
|
||||
SUPPORTED_1000baseT_Full)) {
|
||||
lpagb = phy_read(phydev, MII_STAT1000);
|
||||
|
||||
if(lpagb < 0) {
|
||||
return lpagb;
|
||||
}
|
||||
adv = phy_read(phydev, MII_CTRL1000);
|
||||
|
||||
if(adv < 0) {
|
||||
return adv;
|
||||
}
|
||||
lpagb &= adv << 2;
|
||||
}
|
||||
|
||||
lpa = phy_read(phydev, MII_LPA);
|
||||
|
||||
if(lpa < 0) {
|
||||
return lpa;
|
||||
}
|
||||
adv = phy_read(phydev, MII_ADVERTISE);
|
||||
|
||||
if(adv < 0) {
|
||||
return adv;
|
||||
}
|
||||
lpa &= adv;
|
||||
|
||||
phydev->speed = SPEED_10;
|
||||
phydev->duplex = DUPLEX_HALF;
|
||||
phydev->pause = phydev->asym_pause = 0;
|
||||
|
||||
if(lpagb & (LPA_1000FULL | LPA_1000HALF)) {
|
||||
phydev->speed = SPEED_1000;
|
||||
|
||||
if(lpagb & LPA_1000FULL) {
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
}
|
||||
} else if(lpa & (LPA_100FULL | LPA_100HALF)) {
|
||||
phydev->speed = SPEED_100;
|
||||
|
||||
if(lpa & LPA_100FULL) {
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
}
|
||||
} else {
|
||||
if(lpa & LPA_10FULL) {
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
}
|
||||
}
|
||||
if(phydev->duplex == DUPLEX_FULL){
|
||||
phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
|
||||
phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
int bmcr = phy_read(phydev, MII_BMCR);
|
||||
if(bmcr < 0) {
|
||||
return bmcr;
|
||||
}
|
||||
if(bmcr & BMCR_FULLDPLX) {
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
} else {
|
||||
phydev->duplex = DUPLEX_HALF;
|
||||
}
|
||||
if(bmcr & BMCR_SPEED1000) {
|
||||
phydev->speed = SPEED_1000;
|
||||
} else if(bmcr & BMCR_SPEED100) {
|
||||
phydev->speed = SPEED_100;
|
||||
} else {
|
||||
phydev->speed = SPEED_10;
|
||||
}
|
||||
phydev->pause = phydev->asym_pause = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*************************** (C) COPYRIGHT 2018 HUGE-IC ***** END OF FILE *****/
|
||||
Reference in New Issue
Block a user