Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources

This commit is contained in:
2026-07-06 11:30:13 +08:00
commit e76462eeb7
3451 changed files with 1415300 additions and 0 deletions

View 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>&copy; 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 *****/