rt-thread-official/components/drivers/phy/general.c

350 lines
8.8 KiB
C

/*
* Copyright (c) 2006-2024 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-10-08 zhujiale the first version
*/
#include <rtthread.h>
#include <drivers/phy.h>
#include "general_phy.h"
#define DBG_TAG "rtdm.phy"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static int __genphy_set_adv(int adv,int advertise)
{
adv &= ~(RT_ADVERTISE_ALL | RT_ADVERTISE_100BASE4 | RT_ADVERTISE_PAUSE_CAP |
RT_ADVERTISE_PAUSE_ASYM);
if (advertise & RT_ADVERTISED__10baseT_Half)
adv |= RT_ADVERTISE_10HALF;
if (advertise & RT_ADVERTISED__10baseT_Full)
adv |= RT_ADVERTISE_10FULL;
if (advertise & RT_ADVERTISED__100baseT_Half)
adv |= RT_ADVERTISE_100HALF;
if (advertise & RT_ADVERTISED__100baseT_Full)
adv |= RT_ADVERTISE_100FULL;
if (advertise & RT_ADVERTISED__Pause)
adv |= RT_ADVERTISE_PAUSE_CAP;
if (advertise & RT_ADVERTISED__Asym_Pause)
adv |= RT_ADVERTISE_PAUSE_ASYM;
if (advertise & RT_ADVERTISED__1000baseX_Half)
adv |= RT_ADVERTISE_1000XHALF;
if (advertise & RT_ADVERTISED__1000baseX_Full)
adv |= RT_ADVERTISE_1000XFULL;
return adv;
}
static int __genphy_config_advert(struct rt_phy_device *phydev)
{
rt_uint32_t advertise;
int oldadv, adv, bmsr;
int err, changed = 0;
phydev->advertising &= phydev->supported;
advertise = phydev->advertising;
adv = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ADVERTISE);
oldadv = adv;
if (adv < 0)
return adv;
adv = __genphy_set_adv(adv, advertise);
if (adv != oldadv)
{
err = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ADVERTISE, adv);
if (err < 0)
return err;
changed = 1;
}
bmsr = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
if (bmsr < 0)
return bmsr;
if (!(bmsr & RT_BMSR_ESTATEN))
return changed;
adv = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_CTRL1000);
oldadv = adv;
if (adv < 0)
return adv;
adv &= ~(RT_ADVERTISE_1000FULL | RT_ADVERTISE_1000HALF);
if (phydev->supported & (RT_SUPPORTED_1000baseT_Half |
RT_SUPPORTED_1000baseT_Full))
{
if (advertise & RT_SUPPORTED_1000baseT_Half)
adv |= RT_ADVERTISE_1000HALF;
if (advertise & RT_SUPPORTED_1000baseT_Full)
adv |= RT_ADVERTISE_1000FULL;
}
if (adv != oldadv)
changed = 1;
err = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_CTRL1000, adv);
if (err < 0)
return err;
return changed;
}
int __genphy_restart_aneg(struct rt_phy_device *phydev)
{
int ctl;
ctl = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR);
if (ctl < 0)
return ctl;
ctl |= (RT_BMCR_ANENABLE | RT_BMCR_ANRESTART);
ctl &= ~(RT_BMCR_ISOLATE);
ctl = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR, ctl);
return ctl;
}
int rt_genphy_config_aneg(struct rt_phy_device *phydev)
{
int result;
int err;
int ctl = RT_BMCR_ANRESTART;
if (phydev->autoneg != AUTONEG_ENABLE)
{
phydev->pause = 0;
if (phydev->speed == SPEED_1000)
ctl |= RT_BMCR_SPEED1000;
else if (phydev->speed == SPEED_100)
ctl |= RT_BMCR_SPEED100;
if (phydev->duplex == DUPLEX_FULL)
ctl |= RT_BMCR_FULLDPLX;
err = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR, ctl);
return err;
}
result = __genphy_config_advert(phydev);
if (result < 0)
return result;
if (result == 0)
{
int ctl = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR);
if (ctl < 0)
return ctl;
if (!(ctl & RT_BMCR_ANENABLE) || (ctl & RT_BMCR_ISOLATE))
result = 1;
}
if (result > 0)
result = __genphy_restart_aneg(phydev);
return result;
}
int rt_genphy_update_link(struct rt_phy_device *phydev)
{
unsigned int mii_reg;
mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
if (phydev->link && mii_reg & RT_BMSR_LSTATUS)
return 0;
if ((phydev->autoneg == AUTONEG_ENABLE) &&
!(mii_reg & RT_BMSR_ANEGCOMPLETE))
{
int i = 0;
rt_kprintf("Waiting for PHY auto negotiation to complete");
while (!(mii_reg & RT_BMSR_ANEGCOMPLETE))
{
if (i > (RT_PHY_ANEG_TIMEOUT))
{
LOG_E(" TIMEOUT !\n");
phydev->link = 0;
return -ETIMEDOUT;
}
mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
rt_thread_delay(100);
}
LOG_D(" done\n");
phydev->link = 1;
} else {
mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
if (mii_reg & RT_BMSR_LSTATUS)
phydev->link = 1;
else
phydev->link = 0;
}
return 0;
}
static void __genphy_auto_neg(struct rt_phy_device *phydev,int mii_reg)
{
rt_uint32_t lpa = 0;
int gblpa = 0;
rt_uint32_t estatus = 0;
if (phydev->supported & (RT_SUPPORTED_1000baseT_Full |
RT_SUPPORTED_1000baseT_Half))
{
gblpa = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_STAT1000);
if (gblpa < 0)
{
LOG_D("Could not read RT_MII_STAT1000. Ignoring gigabit capability\n");
gblpa = 0;
}
gblpa &= rt_phy_read(phydev,
RT_MDIO_DEVAD_NONE, RT_MII_CTRL1000) << 2;
}
phydev->speed = SPEED_10;
phydev->duplex = DUPLEX_HALF;
if (gblpa & (RT_PHY_1000BTSR_1000FD | RT_PHY_1000BTSR_1000HD))
{
phydev->speed = SPEED_1000;
if (gblpa & RT_PHY_1000BTSR_1000FD)
phydev->duplex = DUPLEX_FULL;
return ;
}
lpa = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ADVERTISE);
lpa &= rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_LPA);
if (lpa & (RT_LINK_PARTNER__100FULL | RT_LINK_PARTNER__100HALF))
{
phydev->speed = SPEED_100;
if (lpa & RT_LINK_PARTNER__100FULL)
phydev->duplex = DUPLEX_FULL;
} else if (lpa & RT_LINK_PARTNER__10FULL)
{
phydev->duplex = DUPLEX_FULL;
}
if ((mii_reg & RT_BMSR_ESTATEN) && !(mii_reg & RT_BMSR_ERCAP))
estatus = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE,
RT_MII_ESTATUS);
if (estatus & (RT_SUPORT_1000B_XFULL | RT_SUPORT_1000B_XHALF |
RT_SUPORT_1000B_TFULL | RT_SUPORT_1000B_THALF))
{
phydev->speed = SPEED_1000;
if (estatus & (RT_SUPORT_1000B_XFULL | RT_SUPORT_1000B_TFULL))
phydev->duplex = DUPLEX_FULL;
}
}
int rt_genphy_parse_link(struct rt_phy_device *phydev)
{
int mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
if (phydev->autoneg == AUTONEG_ENABLE)
{
__genphy_auto_neg(phydev, mii_reg);
} else {
rt_uint32_t bmcr = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR);
phydev->speed = SPEED_10;
phydev->duplex = DUPLEX_HALF;
if (bmcr & RT_BMCR_FULLDPLX)
phydev->duplex = DUPLEX_FULL;
if (bmcr & RT_BMCR_SPEED1000)
phydev->speed = SPEED_1000;
else if (bmcr & RT_BMCR_SPEED100)
phydev->speed = SPEED_100;
}
return 0;
}
int rt_genphy_config(struct rt_phy_device *phydev)
{
int val;
rt_uint32_t features;
features = (RT_SUPPORTED_TP | RT_SUPPORTED_MII
| RT_SUPPORTED_AUI | RT_SUPPORTED_FIBRE |
RT_SUPPORTED_BNC);
val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR);
if (val < 0)
return val;
if (val & RT_BMSR_ANEGCAPABLE)
features |= RT_SUPPORTED_Autoneg;
if (val & RT_BMSR_100FULL)
features |= RT_SUPPORTED_100baseT_Full;
if (val & RT_BMSR_100HALF)
features |= RT_SUPPORTED_100baseT_Half;
if (val & RT_BMSR_10FULL)
features |= RT_SUPPORTED_10baseT_Full;
if (val & RT_BMSR_10HALF)
features |= RT_SUPPORTED_10baseT_Half;
if (val & RT_BMSR_ESTATEN)
{
val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_ESTATUS);
if (val < 0)
return val;
if (val & RT_SUPORT_1000B_TFULL)
features |= RT_SUPPORTED_1000baseT_Full;
if (val & RT_SUPORT_1000B_THALF)
features |= RT_SUPPORTED_1000baseT_Half;
if (val & RT_SUPORT_1000B_XFULL)
features |= RT_SUPPORTED_1000baseX_Full;
if (val & RT_SUPORT_1000B_XHALF)
features |= RT_SUPPORTED_1000baseX_Half;
}
phydev->supported &= features;
phydev->advertising &= features;
rt_genphy_config_aneg(phydev);
return 0;
}
int rt_genphy_startup(struct rt_phy_device *phydev)
{
int ret;
ret = rt_genphy_update_link(phydev);
if (ret)
return ret;
return rt_genphy_parse_link(phydev);
}