diff --git a/components/drivers/include/drivers/phy.h b/components/drivers/include/drivers/phy.h index c51c494c15..a8dcd08b7f 100644 --- a/components/drivers/include/drivers/phy.h +++ b/components/drivers/include/drivers/phy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -7,18 +7,172 @@ * Date Author Notes * 2020-10-14 wangqiang the first version * 2022-08-17 xjy198903 add 1000M definition + * 2024-10-08 zhujiale add phy v2.0 */ - -#ifndef __PHY_H__ -#define __PHY_H__ - +#ifndef __NET_PHY_H__ +#define __NET_PHY_H__ #include +#include +#ifdef RT_USING_PHY_V2 +#include +#include +#include +#define RT_PHY_FIXED_ID 0xa5a55a5a +#define RT_PHY_NCSI_ID 0xbeefcafe + + +/* Indicates what features are supported by the interface. */ +#define RT_SUPPORTED_10baseT_Half (1 << 0) +#define RT_SUPPORTED_10baseT_Full (1 << 1) +#define RT_SUPPORTED_100baseT_Half (1 << 2) +#define RT_SUPPORTED_100baseT_Full (1 << 3) +#define RT_SUPPORTED_1000baseT_Half (1 << 4) +#define RT_SUPPORTED_1000baseT_Full (1 << 5) +#define RT_SUPPORTED_Autoneg (1 << 6) +#define RT_SUPPORTED_TP (1 << 7) +#define RT_SUPPORTED_AUI (1 << 8) +#define RT_SUPPORTED_MII (1 << 9) +#define RT_SUPPORTED_FIBRE (1 << 10) +#define RT_SUPPORTED_BNC (1 << 11) +#define RT_SUPPORTED_10000baseT_Full (1 << 12) +#define RT_SUPPORTED_Pause (1 << 13) +#define RT_SUPPORTED_Asym_Pause (1 << 14) +#define RT_SUPPORTED_2500baseX_Full (1 << 15) +#define RT_SUPPORTED_Backplane (1 << 16) +#define RT_SUPPORTED_1000baseKX_Full (1 << 17) +#define RT_SUPPORTED_10000baseKX4_Full (1 << 18) +#define RT_SUPPORTED_10000baseKR_Full (1 << 19) +#define RT_SUPPORTED_10000baseR_FEC (1 << 20) +#define RT_SUPPORTED_1000baseX_Half (1 << 21) +#define RT_SUPPORTED_1000baseX_Full (1 << 22) + +#define RT_PHY_FLAG_BROKEN_RESET (1 << 0) /* soft reset not supported */ +#define RT_PHY_DEFAULT_FEATURES (RT_SUPPORTED_Autoneg | RT_SUPPORTED_TP | RT_SUPPORTED_MII) + +#define RT_PHY_10BT_FEATURES (RT_SUPPORTED_10baseT_Half | RT_SUPPORTED_10baseT_Full) + +#define RT_PHY_100BT_FEATURES (RT_SUPPORTED_100baseT_Half | RT_SUPPORTED_100baseT_Full) + +#define RT_PHY_1000BT_FEATURES (RT_SUPPORTED_1000baseT_Half | RT_SUPPORTED_1000baseT_Full) + +#define RT_PHY_BASIC_FEATURES (RT_PHY_10BT_FEATURES | RT_PHY_100BT_FEATURES | RT_PHY_DEFAULT_FEATURES) + +#define RT_PHY_GBIT_FEATURES (RT_PHY_BASIC_FEATURES | RT_PHY_1000BT_FEATURES) + +#define RT_PHY_10G_FEATURES (RT_PHY_GBIT_FEATURES | RT_SUPPORTED_10000baseT_Full) +struct rt_phy_device +{ + struct rt_device parent; + struct mii_bus *bus; + struct rt_phy_driver *drv; + rt_uint32_t phy_id; + rt_uint32_t mmds; + int speed; + int duplex; + int link; + int port; + rt_uint32_t advertising; + rt_uint32_t supported; + rt_bool_t autoneg; + int pause; + rt_ubase_t addr; + rt_bool_t is_c45; + rt_uint32_t flags; + rt_phy_interface interface; + +#ifdef RT_USING_OFW + struct rt_ofw_node *node; +#endif + void *priv; +}; + +struct rt_phy_driver +{ + struct rt_driver parent; + char name[RT_NAME_MAX]; + rt_uint64_t uid; + rt_uint64_t mask; + rt_uint64_t mmds; + rt_uint32_t features; + + int (*probe)(struct rt_phy_device *phydev); + int (*config)(struct rt_phy_device *phydev); + int (*startup)(struct rt_phy_device *phydev); + int (*shutdown)(struct rt_phy_device *phydev); + int (*read)(struct rt_phy_device *phydev, int addr, int devad, int reg); + int (*write)(struct rt_phy_device *phydev, int addr, int devad, int reg, + rt_uint16_t val); + int (*read_mmd)(struct rt_phy_device *phydev, int devad, int reg); + int (*write_mmd)(struct rt_phy_device *phydev, int devad, int reg, + rt_uint16_t val); + + /* driver private data */ + void *data; +}; + +int rt_phy_read(struct rt_phy_device *phydev, int devad, int regnum); +int rt_phy_write(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val); +int rt_phy_read_mmd(struct rt_phy_device *phydev, int devad, int regnum); +int rt_phy_write_mmd(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val); +int rt_phy_reset(struct rt_phy_device *phydev); +int rt_phy_startup(struct rt_phy_device *phydev); +int rt_phy_config(struct rt_phy_device *phydev); +int rt_phy_shutdown(struct rt_phy_device *phydev); +int rt_phy_read_mmd(struct rt_phy_device *phydev, int devad, int regnum); +int rt_phy_set_supported(struct rt_phy_device *phydev, rt_uint32_t max_speed); + +void rt_phy_mmd_start_indirect(struct rt_phy_device *phydev, int devad, int regnum); + +rt_err_t rt_phy_device_register(struct rt_phy_device *pdev); +rt_err_t rt_phy_driver_register(struct rt_phy_driver *pdrv); +rt_err_t rt_ofw_get_phyid(struct rt_ofw_node *np,rt_uint32_t *id); + +struct rt_phy_device *rt_phy_device_create(struct mii_bus *bus, int addr, rt_uint32_t phy_id, rt_bool_t is_c45); +struct rt_phy_device *rt_phy_find_by_mask(struct mii_bus *bus, unsigned int phy_mask); +struct rt_phy_device *rt_ofw_create_phy(struct mii_bus *bus, struct rt_ofw_node *np, int phyaddr); +struct rt_phy_device *rt_phy_get_device(struct mii_bus *bus, struct rt_ofw_node *np, int addr, rt_phy_interface interface); + +#define RT_PHY_DEVICE_REGISTER(phy_dev) \ +static int rt_##phy_dev##_register(void) \ +{ \ + rt_phy_device_register(&phy_dev); \ + return 0; \ +} \ +INIT_PREV_EXPORT(rt_##phy_dev##_register); + +#define RT_PHY_DRIVER_REGISTER(phy_drv) \ +static int rt_##phy_drv##_register(void) \ +{ \ + rt_phy_driver_register(&phy_drv); \ + return 0; \ +} \ +INIT_PREV_EXPORT(rt_##phy_drv##_register); +#endif + +#ifdef RT_USING_PHY #ifdef __cplusplus extern "C" { #endif +struct rt_mdio_bus_ops +{ + rt_bool_t (*init)(void *bus, rt_uint32_t src_clock_hz); + rt_size_t (*read)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); + rt_size_t (*write)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); + rt_bool_t (*uninit)(void *bus); +}; + +struct rt_mdio_bus +{ + void *hw_obj; + char *name; + struct rt_mdio_bus_ops *ops; +}; + +typedef struct rt_mdio_bus rt_mdio_t; + /* Defines the PHY link speed. This is align with the speed for MAC. */ #define PHY_SPEED_10M 0U /* PHY 10M speed. */ #define PHY_SPEED_100M 1U /* PHY 100M speed. */ @@ -67,5 +221,5 @@ rt_err_t rt_hw_phy_register(struct rt_phy_device *phy, const char *name); #ifdef __cplusplus } #endif - -#endif /* __PHY_H__*/ +#endif +#endif diff --git a/components/drivers/include/drivers/phy_mdio.h b/components/drivers/include/drivers/phy_mdio.h deleted file mode 100644 index b7677d31d1..0000000000 --- a/components/drivers/include/drivers/phy_mdio.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2006-2023, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2020-10-14 wangqiang the first version - */ - -#ifndef __MDIO_H__ -#define __MDIO_H__ - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - - -struct rt_mdio_bus_ops -{ - rt_bool_t (*init)(void *bus, rt_uint32_t src_clock_hz); - rt_size_t (*read)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); - rt_size_t (*write)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size); - rt_bool_t (*uninit)(void *bus); -}; - -struct rt_mdio_bus -{ - void *hw_obj; - char *name; - struct rt_mdio_bus_ops *ops; -}; - -typedef struct rt_mdio_bus rt_mdio_t; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index c8ed3c0489..a9a8515b7f 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -107,7 +107,6 @@ extern "C" { #ifdef RT_USING_PHY #include "drivers/phy.h" -#include "drivers/phy_mdio.h" #endif /* RT_USING_PHY */ #ifdef RT_USING_SDIO diff --git a/components/drivers/phy/Kconfig b/components/drivers/phy/Kconfig index bb88a41526..92dcba8c4e 100644 --- a/components/drivers/phy/Kconfig +++ b/components/drivers/phy/Kconfig @@ -1,3 +1,8 @@ config RT_USING_PHY bool "Using ethernet phy device drivers" default n + +config RT_USING_PHY_V2 + bool "Using phy device and mii bus v2" + depends on !RT_USING_PHY + default n diff --git a/components/drivers/phy/SConscript b/components/drivers/phy/SConscript index ec59a7aff6..ba227c72d4 100644 --- a/components/drivers/phy/SConscript +++ b/components/drivers/phy/SConscript @@ -1,10 +1,18 @@ from building import * cwd = GetCurrentDir() +CPPPATH = [cwd, cwd + '/../include'] src = Glob('*.c') if GetDepend('RT_USING_OFW') == False: SrcRemove(src, ['ofw.c']) -CPPPATH = [cwd,cwd + '/../include'] -group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_PHY'], CPPPATH = CPPPATH) + +if GetDepend('RT_USING_PHY_V2') == False: + SrcRemove(src, ['general.c','mdio.c','ofw.c']) + +if GetDepend('RT_USING_PHY_V2') == False: + if GetDepend('RT_USING_PHY') == False: + SrcRemove(src, ['phy.c']) + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/phy/general.c b/components/drivers/phy/general.c new file mode 100644 index 0000000000..969c38c368 --- /dev/null +++ b/components/drivers/phy/general.c @@ -0,0 +1,349 @@ +/* + * 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 +#include +#include "general_phy.h" +#define DBG_TAG "rtdm.phy" +#define DBG_LVL DBG_INFO +#include + +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); +} diff --git a/components/drivers/phy/general_phy.h b/components/drivers/phy/general_phy.h new file mode 100644 index 0000000000..37acb3ef53 --- /dev/null +++ b/components/drivers/phy/general_phy.h @@ -0,0 +1,143 @@ + +/* + * 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 + */ +#ifndef __PHY_GENERAL_H__ +#define __PHY_GENERAL_H__ +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_10000 10000 +/* Advertisement control register. */ +#define RT_ADVERTISE_SLCT 0x001f /* Selector bits */ +#define RT_ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define RT_ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define RT_ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define RT_ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define RT_ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define RT_ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define RT_ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define RT_ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define RT_ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define RT_ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define RT_ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define RT_ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ +#define RT_ADVERTISE_RESV 0x1000 /* Unused... */ +#define RT_ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define RT_ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define RT_ADVERTISE_NPAGE 0x8000 /* Next page bit */ + +#define RT_ADVERTISE_FULL (RT_ADVERTISE_100FULL | RT_ADVERTISE_10FULL | \ + RT_ADVERTISE_CSMA) +#define RT_ADVERTISE_ALL (RT_ADVERTISE_10HALF | RT_ADVERTISE_10FULL | \ + RT_ADVERTISE_100HALF | RT_ADVERTISE_100FULL) + +/* Indicates what features are advertised by the interface. */ +#define RT_ADVERTISED__10baseT_Half (1 << 0) +#define RT_ADVERTISED__10baseT_Full (1 << 1) +#define RT_ADVERTISED__100baseT_Half (1 << 2) +#define RT_ADVERTISED__100baseT_Full (1 << 3) +#define RT_ADVERTISED__1000baseT_Half (1 << 4) +#define RT_ADVERTISED__1000baseT_Full (1 << 5) +#define RT_ADVERTISED__Autoneg (1 << 6) +#define RT_ADVERTISED__TP (1 << 7) +#define RT_ADVERTISED__AUI (1 << 8) +#define RT_ADVERTISED__MII (1 << 9) +#define RT_ADVERTISED__FIBRE (1 << 10) +#define RT_ADVERTISED__BNC (1 << 11) +#define RT_ADVERTISED__10000baseT_Full (1 << 12) +#define RT_ADVERTISED__Pause (1 << 13) +#define RT_ADVERTISED__Asym_Pause (1 << 14) +#define RT_ADVERTISED__2500baseX_Full (1 << 15) +#define RT_ADVERTISED__Backplane (1 << 16) +#define RT_ADVERTISED__1000baseKX_Full (1 << 17) +#define RT_ADVERTISED__10000baseKX4_Full (1 << 18) +#define RT_ADVERTISED__10000baseKR_Full (1 << 19) +#define RT_ADVERTISED__10000baseR_FEC (1 << 20) +#define RT_ADVERTISED__1000baseX_Half (1 << 21) +#define RT_ADVERTISED__1000baseX_Full (1 << 22) + +/* Basic mode status register. */ +#define RT_BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define RT_BMSR_JCD 0x0002 /* Jabber detected */ +#define RT_BMSR_LSTATUS 0x0004 /* Link status */ +#define RT_BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define RT_BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define RT_BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define RT_BMSR_RESV 0x00c0 /* Unused... */ +#define RT_BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ +#define RT_BMSR_100HALF2 0x0200 /* Can do 100BASE-T2 HDX */ +#define RT_BMSR_100FULL2 0x0400 /* Can do 100BASE-T2 FDX */ +#define RT_BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define RT_BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define RT_BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define RT_BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +#define RT_BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ +/* 1000BASE-T Control register */ +#define RT_ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define RT_ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ +#define CTL1000_AS_MASTER 0x0800 +#define CTL1000_ENABLE_MASTER 0x1000 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 + +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 +#define RT_PHY_1000BTSR_MSCF 0x8000 +#define RT_PHY_1000BTSR_MSCR 0x4000 +#define RT_PHY_1000BTSR_LRS 0x2000 +#define RT_PHY_1000BTSR_RRS 0x1000 +#define RT_PHY_1000BTSR_1000FD 0x0800 +#define RT_PHY_1000BTSR_1000HD 0x0400 + +/* Link partner ability register. */ +#define RT_LINK_PARTNER__SLCT 0x001f /* Same as advertise selector */ +#define RT_LINK_PARTNER__10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define RT_LINK_PARTNER__1000XFULL 0x0020 /* Can do 1000BASE-X full-duplex */ +#define RT_LINK_PARTNER__10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define RT_LINK_PARTNER__1000XHALF 0x0040 /* Can do 1000BASE-X half-duplex */ +#define RT_LINK_PARTNER__100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define RT_LINK_PARTNER__1000XPAUSE 0x0080 /* Can do 1000BASE-X pause */ +#define RT_LINK_PARTNER__100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define RT_LINK_PARTNER__1000XPAUSE_ASYM 0x0100 /* Can do 1000BASE-X pause asym*/ +#define RT_LINK_PARTNER__100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define RT_LINK_PARTNER__PAUSE_CAP 0x0400 /* Can pause */ +#define RT_LINK_PARTNER__PAUSE_ASYM 0x0800 /* Can pause asymetrically */ +#define RT_LINK_PARTNER__RESV 0x1000 /* Unused... */ +#define RT_LINK_PARTNER__RFAULT 0x2000 /* Link partner faulted */ +#define RT_LINK_PARTNER__LPACK 0x4000 /* Link partner acked us */ +#define RT_LINK_PARTNER__NPAGE 0x8000 /* Next page bit */ + +#define RT_LINK_PARTNER__DUPLEX (RT_LINK_PARTNER__10FULL | RT_LINK_PARTNER__100FULL) +#define RT_LINK_PARTNER__100 (RT_LINK_PARTNER__100FULL | RT_LINK_PARTNER__100HALF | RT_LINK_PARTNER__100BASE4) +/* Expansion register for auto-negotiation. */ +#define RT_EXPANSION_REG_NWAY 0x0001 /* Can do N-way auto-nego */ +#define RT_EXPANSION_REG_LCWP 0x0002 /* Got new RX page code word */ +#define RT_EXPANSION_REG_ENABLENPAGE 0x0004 /* This enables npage words */ +#define RT_EXPANSION_REG_NPCAPABLE 0x0008 /* Link partner supports npage */ +#define RT_EXPANSION_REG_MFAULTS 0x0010 /* Multiple faults detected */ +#define RT_EXPANSION_REG_RESV 0xffe0 /* Unused... */ + +#define RT_SUPORT_1000B_XFULL 0x8000 /* Can do 1000BX Full */ +#define RT_SUPORT_1000B_XHALF 0x4000 /* Can do 1000BX Half */ +#define RT_SUPORT_1000B_TFULL 0x2000 /* Can do 1000BT Full */ +#define RT_SUPORT_1000B_THALF 0x1000 /* Can do 1000BT Half */ +#define RT_PHY_ANEG_TIMEOUT 4000 +struct rt_phy_device; + +int rt_genphy_parse_link(struct rt_phy_device *phydev); +int rt_genphy_config_aneg(struct rt_phy_device *phydev); +int rt_genphy_update_link(struct rt_phy_device *phydev); +int rt_genphy_startup(struct rt_phy_device *phydev); +int rt_genphy_config(struct rt_phy_device *phydev); +#endif diff --git a/components/drivers/phy/mdio.c b/components/drivers/phy/mdio.c new file mode 100644 index 0000000000..7f9be35478 --- /dev/null +++ b/components/drivers/phy/mdio.c @@ -0,0 +1,75 @@ +/* + * 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 "mdio.h" + +static struct rt_list_node mdio_list; + +struct mii_bus *rt_mdio_get_bus_by_name(const char *busname) +{ + struct rt_list_node *entry; + struct mii_bus *bus; + + if (!busname) + { + rt_kprintf("NULL bus name!\n"); + return RT_NULL; + } + + rt_list_for_each(entry, &mdio_list) + { + bus = rt_container_of(entry, struct mii_bus, node); + if (!strcmp(bus->name, busname)) + return bus; + } + + return RT_NULL; +} + +struct mii_bus *rt_mdio_alloc(void) +{ + struct mii_bus *mii; + mii = rt_malloc(sizeof(struct mii_bus)); + if (!mii) + return RT_NULL; + + rt_list_init(&mii->node); + return mii; +} + +rt_err_t rt_mdio_register(struct mii_bus *bus) +{ + if (!bus) + return -RT_ERROR; + + if(rt_mdio_get_bus_by_name(bus->name)) + { + rt_kprintf("mdio bus %s already exist!\n", bus->name); + return -RT_ERROR; + } + + rt_list_insert_before(&mdio_list, &bus->node); + return RT_EOK; +} + +rt_err_t rt_mdio_unregister(struct mii_bus *bus) +{ + if (!bus) + return -RT_ERROR; + + rt_list_remove(&bus->node); + return RT_EOK; +} + +static int mdio_init(void) +{ + rt_list_init(&mdio_list); + return 0; +} +INIT_CORE_EXPORT(mdio_init); diff --git a/components/drivers/phy/mdio.h b/components/drivers/phy/mdio.h new file mode 100644 index 0000000000..89e98d4b7a --- /dev/null +++ b/components/drivers/phy/mdio.h @@ -0,0 +1,98 @@ +/* + * 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 + */ +#ifndef __PHY_MDIO_H__ +#define __PHY_MDIO_H__ +#include +#include + +#define RT_MDIO_DEVAD_NONE (-1) +#define RT_MDIO_MMD_PMAPMD 1 /* Physical Medium Attachment/ + * Physical Medium Dependent */ +#define RT_MDIO_MMD_WIS 2 /* WAN Interface Sublayer */ +#define RT_MDIO_MMD_PCS 3 /* Physical Coding Sublayer */ +#define RT_MDIO_MMD_PHYXS 4 /* PHY Extender Sublayer */ +#define RT_MDIO_MMD_DTEXS 5 /* DTE Extender Sublayer */ +#define RT_MDIO_MMD_TC 6 /* Transmission Convergence */ +#define RT_MDIO_MMD_AN 7 /* Auto-Negotiation */ +#define RT_MDIO_MMD_C22EXT 29 /* Clause 22 extension */ +#define RT_MDIO_MMD_VEND1 30 /* Vendor specific 1 */ +#define RT_MDIO_MMD_VEND2 31 /* Vendor specific 2 */ + +#define RT_MII_BMCR 0x00 /* Basic mode control register */ +#define RT_MII_BMSR 0x01 /* Basic mode status register */ +#define RT_MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define RT_MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define RT_MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define RT_MII_LPA 0x05 /* Link partner ability reg */ +#define RT_MII_EXPANSION 0x06 /* Expansion register */ +#define RT_MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define RT_MII_STAT1000 0x0a /* 1000BASE-T status */ +#define RT_MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define RT_MII_MMD_DATA 0x0e /* MMD Access Data Register */ +#define RT_MII_ESTATUS 0x0f /* Extended Status */ +#define RT_MII_DCOUNTER 0x12 /* Disconnect counter */ +#define RT_MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define RT_MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define RT_MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define RT_MII_SREVISION 0x16 /* Silicon revision */ +#define RT_MII_RESV1 0x17 /* Reserved... */ +#define RT_MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define RT_MII_PHYADDR 0x19 /* PHY address */ +#define RT_MII_RESV2 0x1a /* Reserved... */ +#define RT_MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define RT_MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ +#define RT_BMCR_RESV 0x003f /* Unused... */ +#define RT_BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ +#define RT_BMCR_CTST 0x0080 /* Collision test */ +#define RT_BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define RT_BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define RT_BMCR_ISOLATE 0x0400 /* Isolate data paths from MII */ +#define RT_BMCR_PDOWN 0x0800 /* Enable low power state */ +#define RT_BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define RT_BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define RT_BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define RT_BMCR_RESET 0x8000 /* Reset to default state */ +#define RT_BMCR_SPEED10 0x0000 /* Select 10Mbps */ + +#define RT_MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ +#define RT_MII_MMD_CTRL_ADDR 0x0000 /* Address */ +#define RT_MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ +#define RT_MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ +#define RT_MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ + + +#define RT_PHY_MAX 32 + +struct mii_bus +{ + struct rt_list_node node; + char name[RT_NAME_MAX]; + void* priv; + int (*read)(struct mii_bus* bus, int addr, int devad, int reg); + int (*write)(struct mii_bus* bus, int addr, int devad, int reg, rt_uint16_t val); + /** @read_c45: Perform a C45 read transfer on the bus */ + int (*read_c45)(struct mii_bus* bus, int addr, int devad, int reg); + /** @write_c45: Perform a C45 write transfer on the bus */ + int (*write_c45)(struct mii_bus* bus, int addr, int devad, int reg, rt_uint16_t val); + int (*reset)(struct mii_bus* bus); + struct rt_phy_device* phymap[RT_PHY_MAX]; + rt_uint32_t phy_mask; + int reset_delay_us; + int reset_post_delay_us; +}; + +rt_err_t rt_mdio_register(struct mii_bus* bus); +rt_err_t rt_mdio_unregister(struct mii_bus* bus); + +struct mii_bus* rt_mdio_get_bus_by_name(const char* busname); +struct mii_bus* rt_mdio_alloc(void); +#endif diff --git a/components/drivers/phy/ofw.c b/components/drivers/phy/ofw.c index 189695c7ea..77429ec4c4 100644 --- a/components/drivers/phy/ofw.c +++ b/components/drivers/phy/ofw.c @@ -8,6 +8,11 @@ * 2024-09-25 zhujiale the first version */ #include +#include +#include +#define DBG_TAG "rtdm.phy" +#define DBG_LVL DBG_INFO +#include #include "ofw.h" static const char* const rt_phy_modes[] = @@ -100,3 +105,52 @@ rt_err_t rt_ofw_get_mac_addr(struct rt_ofw_node *np, rt_uint8_t *addr) return -RT_ERROR; } + +rt_err_t rt_ofw_get_phyid(struct rt_ofw_node *np,rt_uint32_t *id) +{ + const char *phy_id; + unsigned int upper, lower; + int ret; + + ret = rt_ofw_prop_read_string(np,"compatible",&phy_id); + if (ret) + return ret; + + ret = sscanf(phy_id,"ethernet-phy-id%4x.%4x",&upper, &lower); + if(ret != 2) + return -RT_ERROR; + + *id = ((upper & 0xffff) << 16) | (lower & 0xffff); + return RT_EOK; + +} +struct rt_phy_device *rt_ofw_create_phy(struct mii_bus *bus,struct rt_ofw_node *np,int phyaddr) +{ + struct rt_phy_device *dev = RT_NULL; + struct rt_ofw_node *phy_node; + int ret; + rt_uint32_t id = 0xffff; + + phy_node = rt_ofw_parse_phandle(np, "phy-handle", 0); + if (!phy_node) + { + LOG_D("Failed to find phy-handle"); + return RT_NULL; + } + + ret = rt_ofw_get_phyid(np, &id); + if (ret) + { + LOG_D("Failed to read eth PHY id, err: %d\n", ret); + return RT_NULL; + } + + LOG_D("Found a PHY id: 0x%x\n", id); + + dev = rt_phy_device_create(bus, phyaddr, id, RT_FALSE); + + if(dev) + dev->node = phy_node; + + return dev; +} diff --git a/components/drivers/phy/ofw.h b/components/drivers/phy/ofw.h index 30aa77088d..d846aa4a34 100644 --- a/components/drivers/phy/ofw.h +++ b/components/drivers/phy/ofw.h @@ -54,4 +54,5 @@ typedef enum rt_err_t rt_ofw_get_mac_addr(struct rt_ofw_node *np, rt_uint8_t *addr); rt_err_t rt_ofw_get_mac_addr_by_name(struct rt_ofw_node *np, const char *name, rt_uint8_t *addr); rt_err_t rt_ofw_get_interface(struct rt_ofw_node *np, rt_phy_interface *interface); + #endif diff --git a/components/drivers/phy/phy.c b/components/drivers/phy/phy.c index 363b483c58..47c35c2103 100644 --- a/components/drivers/phy/phy.c +++ b/components/drivers/phy/phy.c @@ -1,22 +1,21 @@ - /* - * Copyright (c) 2006-2023, RT-Thread Development Team + * Copyright (c) 2006-2024 RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: - * Date Author Notes - * 2020-09-27 wangqiang first version + * Date Author Notes + * 2020-09-27 wangqiang first version + * 2024-10-08 zhujiale add phy v2.0 */ #include #include -#include #include - -#define DBG_TAG "PHY" +#define DBG_TAG "rtdm.phy" #define DBG_LVL DBG_INFO #include +#ifdef RT_USING_PHY static rt_ssize_t phy_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count) { @@ -74,3 +73,503 @@ rt_err_t rt_hw_phy_register(struct rt_phy_device *phy, const char *name) return ret; } +#endif + +#ifdef RT_USING_PHY_V2 +int rt_phy_set_supported(struct rt_phy_device *phydev, rt_uint32_t max_speed) +{ + phydev->supported &= RT_PHY_DEFAULT_FEATURES; + + switch (max_speed) + { + default: + return -ENOTSUP; + case SPEED_1000: + phydev->supported |= RT_PHY_1000BT_FEATURES; + case SPEED_100: + phydev->supported |= RT_PHY_100BT_FEATURES; + case SPEED_10: + phydev->supported |= RT_PHY_10BT_FEATURES; + } + + return 0; +} + +int rt_phy_read(struct rt_phy_device *phydev, int devad, int regnum) +{ + struct mii_bus *bus = phydev->bus; + if(phydev->is_c45) + { + if(bus->read_c45) + return bus->read_c45(bus, phydev->addr, devad, regnum); + } + + if( bus && bus->read ) + return bus->read(bus, phydev->addr, devad, regnum); + + LOG_D("no read function\n"); + return -1; +} + +int rt_phy_write(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val) +{ + struct mii_bus *bus = phydev->bus; + if(phydev->is_c45) + { + if(bus->write_c45) + return bus->write_c45(bus, phydev->addr, devad, regnum, val); + } + if( bus && bus->write ) + return bus->write(bus, phydev->addr, devad, regnum, val); + + LOG_D("no write function\n"); + return -1; +} + +int rt_phy_startup(struct rt_phy_device *phydev) +{ + if(!phydev->drv) + { + LOG_D("PHY device hace no driver\n"); + return -1; + } + + if (phydev->drv->startup) + return phydev->drv->startup(phydev); + + LOG_D("phy startup err\n"); + return -1; +} + +int rt_phy_config(struct rt_phy_device *phydev) +{ + if(!phydev->drv) + { + LOG_D("PHY device hace no driver\n"); + return -1; + } + + if (phydev->drv->config) + return phydev->drv->config(phydev); + + LOG_D("no config function\n"); + return -1; +} + +int rt_phy_shutdown(struct rt_phy_device *phydev) +{ + if(!phydev->drv) + { + LOG_D("PHY device hace no driver\n"); + return -1; + } + + if (phydev->drv->shutdown) + phydev->drv->shutdown(phydev); + + LOG_D("no shutdown function\n"); + return -1; +} + +void rt_phy_mmd_start_indirect(struct rt_phy_device *phydev, int devad, int regnum) +{ + /* Write the desired MMD Devad */ + rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_DATA, regnum); + + /* Select the Function : DATA with no post increment */ + rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_CTRL, + (devad | RT_MII_MMD_CTRL_NOINCR)); +} + +int rt_phy_read_mmd(struct rt_phy_device *phydev, int devad, int regnum) +{ + struct rt_phy_driver *drv = phydev->drv; + + if (regnum > (rt_uint16_t)~0 || devad > 32 || !drv) + return -EINVAL; + + if (drv->read_mmd) + return drv->read_mmd(phydev, devad, regnum); + + if ((drv->features & RT_PHY_10G_FEATURES) == RT_PHY_10G_FEATURES || + devad == RT_MDIO_DEVAD_NONE || !devad) + return rt_phy_read(phydev, devad, regnum); + + rt_phy_mmd_start_indirect(phydev, devad, regnum); + + return rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_DATA); +} + +int rt_phy_write_mmd(struct rt_phy_device *phydev, int devad, int regnum, rt_uint16_t val) +{ + struct rt_phy_driver *drv = phydev->drv; + + if (regnum > (rt_uint16_t)~0 || devad > 32 || !drv) + return -EINVAL; + + if (drv->write_mmd) + return drv->write_mmd(phydev, devad, regnum, val); + + if ((drv->features & RT_PHY_10G_FEATURES) == RT_PHY_10G_FEATURES || + devad == RT_MDIO_DEVAD_NONE || !devad) + return rt_phy_write(phydev, devad, regnum, val); + + rt_phy_mmd_start_indirect(phydev, devad, regnum); + + return rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RT_MII_MMD_DATA, val); +} + +int rt_phy_reset(struct rt_phy_device *phydev) +{ + int reg; + int timeout = 500; + int devad = RT_MDIO_DEVAD_NONE; + + if (phydev->flags & RT_PHY_FLAG_BROKEN_RESET) + return 0; + + if (rt_phy_write(phydev, devad, RT_MII_BMCR, RT_BMCR_RESET) < 0) + { + LOG_D("PHY reset failed\n"); + return -1; + } + + reg = rt_phy_read(phydev, devad, RT_MII_BMCR); + while ((reg & RT_BMCR_RESET) && timeout--) + { + reg = rt_phy_read(phydev, devad, RT_MII_BMCR); + + if (reg < 0) + { + LOG_D("PHY status read failed\n"); + return -1; + } + rt_thread_mdelay(1); + } + + if (reg & RT_BMCR_RESET) + { + LOG_D("PHY reset timed out\n"); + return -1; + } + + return 0; +} + +static struct rt_bus rt_phy_bus; + +/** + * @brief create a phy device + * + * Creates a new PHY device based on the given bus, address, PHY ID, and whether Clause 45 is supported. + * + * @param bus the pointer to the bus + * @param addr PHY device address + * @param phy_id PHY device id + * @param is_c45 if suport Clause 45 + * + * @return if create success return the phy device pointer,else return RT_NULL + */ +struct rt_phy_device *rt_phy_device_create(struct mii_bus *bus, int addr, + rt_uint32_t phy_id, rt_bool_t is_c45) +{ + struct rt_phy_device *dev = rt_malloc(sizeof(struct rt_phy_device)); + + if (!dev) + { + LOG_E("Failed to allocate PHY device for %s:%d\n", + bus ? bus->name : "(null bus)", addr); + return RT_NULL; + } + + memset(dev, 0, sizeof(*dev)); + + dev->duplex = -1; + dev->link = 0; + dev->interface = RT_PHY_INTERFACE_MODE_NA; +#ifdef RT_USING_OFW + dev->node = RT_NULL; +#endif + dev->autoneg = RT_TRUE; + + dev->addr = addr; + dev->phy_id = phy_id; + dev->is_c45 = is_c45; + dev->bus = bus; + + if(rt_phy_device_register(dev)) + { + LOG_D("register phy device filed") + } + + if (addr >= 0 && addr < RT_PHY_MAX && phy_id != RT_PHY_FIXED_ID && + phy_id != RT_PHY_NCSI_ID) + bus->phymap[addr] = dev; + + return dev; +} + +/** + * @brief get phy id + * + * get phy id by read the register 2 and 3 of PHY device, + * Register of the MII management interface stipulates that + * register 2 contains thehigh 16 bits of the PHY’s identifier + * the register 3 contains the low 16 bits of the PHY’s identifier + * + * @param bus MII bus pointer + * @param addr PHY device address + * @param devad dev addr if be set to zero it means c22 mode,else it means c45 mode + * @param phy_id the phy id which will be read + * + * @return if read success return 0,else return -RT_EIO + */ +static int get_phy_id(struct mii_bus *bus, int addr, int devad, rt_uint32_t *phy_id) +{ + int phy_reg; + + phy_reg = bus->read(bus, addr, devad, RT_MII_PHYSID1); + + if (phy_reg < 0) + return -RT_EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + + phy_reg = bus->read(bus, addr, devad, RT_MII_PHYSID2); + + if (phy_reg < 0) + return -RT_EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} + +/** + * @brief create phy device by mask + * + * @param bus MII bus pointer + * @param phy_mask the mask which phy addr corresponding will be set 1 + * @param devad dev addr if be set to zero it means c22 mode,else it means c45 mode + * + * @return if create success return the phy device pointer,if create fail return NULL + */ +static struct rt_phy_device *create_phy_by_mask(struct mii_bus *bus, unsigned int phy_mask,int devad) +{ + rt_uint32_t id = 0xffffffff; + rt_bool_t is_c45; + + while (phy_mask) + { + int addr = __rt_ffs(phy_mask) - 1; + int r = get_phy_id(bus, addr, devad, &id); + + if (r == 0 && id == 0) + { + phy_mask &= ~(1 << addr); + continue; + } + + if (r == 0 && (id & 0x1fffffff) != 0x1fffffff) + { + is_c45 = (devad == RT_MDIO_DEVAD_NONE) ? RT_FALSE : RT_TRUE; + return rt_phy_device_create(bus, addr, id, is_c45); + } + + } + return RT_NULL; +} + +/** + * @brief create phy device by mask + * + * it will create phy device by c22 mode or c45 mode + * + * @param bus mii bus pointer + * @param phy_mask PHY mask it depend on the phy addr, the phy addr corresponding will be set 1 + * + * @return if create success return the phy device pointer,if create fail return NULL + */ +static struct rt_phy_device *rt_phydev_create_by_mask(struct mii_bus *bus, unsigned int phy_mask) +{ + struct rt_phy_device *phy; + /* + *The bit of devad is dev addr which is the new features in c45 + *so if devad equal to zero it means it is c22 mode ,and if not + *equal to zero it means it is c45 mode,which include PMD/PMA , + *WIS ,PCS,PHY XS,PHY XS .... + */ + int devad[] = { + /* Clause-22 */ + RT_MDIO_DEVAD_NONE, + /* Clause-45 */ + RT_MDIO_MMD_PMAPMD, + RT_MDIO_MMD_WIS, + RT_MDIO_MMD_PCS, + RT_MDIO_MMD_PHYXS, + RT_MDIO_MMD_VEND1, + }; + + for (int i = 0; i < sizeof(devad)/sizeof(devad[0]); i++) + { + phy = create_phy_by_mask(bus, phy_mask, devad[i]); + if(phy) + return phy; + } + + return RT_NULL; +} + +struct rt_phy_device *rt_phy_find_by_mask(struct mii_bus *bus, unsigned int phy_mask) +{ + struct rt_phy_device *phy; + unsigned int mask = phy_mask; + unsigned int addr; + if (bus->reset) + { + bus->reset(bus); + + rt_thread_mdelay(15); + } + + while (mask) + { + /* + *Whichever bit of the mask is the 1, + *which bit is the addr as the array subscript to search + *such as mask = 1110 ,this loop will search for subscript + *1,2,3,if the array subscript is not null then return it, + *if the array subscript is null then continue to search + */ + addr = __rt_ffs(mask) - 1; + + if (bus->phymap[addr]) + return bus->phymap[addr]; + + mask &= ~(1U << addr); + } + /*ifcan't find phy device, create a new phy device*/ + phy = rt_phydev_create_by_mask(bus, phy_mask); + + return phy; + +} + +/** + * @brief get phy device by given mii bus, node and address + * @param bus MII bus pointer + * @param np the dtb node of device which need to get phy device + * @param addr address of phy device + * @param interface interface of phy device + * + * @return phy device pointer or NULL if not found + */ +struct rt_phy_device *rt_phy_get_device(struct mii_bus *bus,struct rt_ofw_node *np, int addr,rt_phy_interface interface) +{ + struct rt_phy_device *phy = RT_NULL; + unsigned int phy_mask = addr? 1 << addr:0xffffffff; + +#ifdef RT_USING_OFW + if(np) + phy = rt_ofw_create_phy(bus,np,addr); +#endif + if(!phy) + phy = rt_phy_find_by_mask(bus,phy_mask); + + if(phy) + { + rt_phy_reset(phy); + phy->interface = interface; + return phy; + } + + LOG_D("PHY device get failed"); + return RT_NULL; +} + +static struct rt_phy_driver genphy = { + .uid = 0xffffffff, + .mask = 0xffffffff, + .name = "Generic PHY", + .features = RT_PHY_GBIT_FEATURES | RT_SUPPORTED_MII | + RT_SUPPORTED_AUI | RT_SUPPORTED_FIBRE | + RT_SUPPORTED_BNC, + .config = rt_genphy_config, + .startup = rt_genphy_startup, +}; +RT_PHY_DRIVER_REGISTER(genphy); + +rt_err_t rt_phy_device_register(struct rt_phy_device *pdev) +{ + rt_err_t err; + RT_ASSERT(pdev != RT_NULL); + err = rt_bus_add_device(&rt_phy_bus, &pdev->parent); + if (err) + { + return err; + } + if(!pdev->drv) + pdev->drv = &genphy; + + return RT_EOK; +} + +rt_err_t rt_phy_driver_register(struct rt_phy_driver *pdrv) +{ + RT_ASSERT(pdrv != RT_NULL); + + pdrv->parent.bus = &rt_phy_bus; +#if RT_NAME_MAX > 0 + rt_strcpy(pdrv->parent.parent.name, pdrv->name); +#else + pdrv->parent.parent.name = pdrv->name; +#endif + + return rt_driver_register(&pdrv->parent); +} + +static rt_bool_t phy_match(rt_driver_t drv, rt_device_t dev) +{ + struct rt_phy_device *pdev = rt_container_of(dev, struct rt_phy_device, parent); + struct rt_phy_driver *pdrv = rt_container_of(drv, struct rt_phy_driver, parent); + if ((pdrv->uid & pdrv->mask) == (pdev->phy_id & pdrv->mask)) + return RT_TRUE; + + return RT_FALSE; + +} + +static rt_err_t phy_probe(rt_device_t dev) +{ + rt_err_t err = RT_EOK; + struct rt_phy_driver *pdrv = rt_container_of(dev->drv, struct rt_phy_driver, parent); + struct rt_phy_device *pdev = rt_container_of(dev, struct rt_phy_device, parent); + pdev->drv = pdrv; + pdev->advertising = pdev->drv->features; + pdev->supported = pdev->drv->features; + + pdev->mmds = pdev->drv->mmds; + if(pdrv->probe) + err = pdrv->probe(pdev); + + return err; +} + +static struct rt_bus rt_phy_bus = +{ + .name = "phy", + .match = phy_match, + .probe = phy_probe, +}; + +static int rt_phy_bus_init(void) +{ + rt_bus_register(&rt_phy_bus); + + return 0; +} +INIT_CORE_EXPORT(rt_phy_bus_init); +#endif