mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2025-01-28 10:40:26 +08:00
bdf4da8ee1
old phy bus is too simple add phy_bus is not adapt rt_bus framework,so writer a stronger phy bus framework. here is my commit message: add mdio bus and phy bus to kernel,the phy bus use rt_bus framewok ,driver writer can write phy_driver first .when mac driver need to use phy they can register phy_device and pjhy_devie will serach for driver which match by uid and mask,if no driver match with the device that you register,phy_bus will return the genphy to you device,the genphy driver is the general driver for phy,so you can use it but it can not support the capcity of chip it may be cause performance is not up to peak
350 lines
8.8 KiB
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);
|
|
}
|