[components][driver]add phy and mdio bus

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
This commit is contained in:
zhuzhuzhu 2024-11-20 10:34:03 +08:00 committed by GitHub
parent b023c15256
commit bdf4da8ee1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1402 additions and 60 deletions

View File

@ -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 * SPDX-License-Identifier: Apache-2.0
* *
@ -7,18 +7,172 @@
* Date Author Notes * Date Author Notes
* 2020-10-14 wangqiang the first version * 2020-10-14 wangqiang the first version
* 2022-08-17 xjy198903 add 1000M definition * 2022-08-17 xjy198903 add 1000M definition
* 2024-10-08 zhujiale add phy v2.0
*/ */
#ifndef __NET_PHY_H__
#ifndef __PHY_H__ #define __NET_PHY_H__
#define __PHY_H__
#include <rtthread.h> #include <rtthread.h>
#include <drivers/core/driver.h>
#ifdef RT_USING_PHY_V2
#include <ofw.h>
#include <mdio.h>
#include <general_phy.h>
#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 #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #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. */ /* Defines the PHY link speed. This is align with the speed for MAC. */
#define PHY_SPEED_10M 0U /* PHY 10M speed. */ #define PHY_SPEED_10M 0U /* PHY 10M speed. */
#define PHY_SPEED_100M 1U /* PHY 100M 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 #ifdef __cplusplus
} }
#endif #endif
#endif
#endif /* __PHY_H__*/ #endif

View File

@ -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 <rtthread.h>
#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

View File

@ -107,7 +107,6 @@ extern "C" {
#ifdef RT_USING_PHY #ifdef RT_USING_PHY
#include "drivers/phy.h" #include "drivers/phy.h"
#include "drivers/phy_mdio.h"
#endif /* RT_USING_PHY */ #endif /* RT_USING_PHY */
#ifdef RT_USING_SDIO #ifdef RT_USING_SDIO

View File

@ -1,3 +1,8 @@
config RT_USING_PHY config RT_USING_PHY
bool "Using ethernet phy device drivers" bool "Using ethernet phy device drivers"
default n default n
config RT_USING_PHY_V2
bool "Using phy device and mii bus v2"
depends on !RT_USING_PHY
default n

View File

@ -1,10 +1,18 @@
from building import * from building import *
cwd = GetCurrentDir() cwd = GetCurrentDir()
CPPPATH = [cwd, cwd + '/../include']
src = Glob('*.c') src = Glob('*.c')
if GetDepend('RT_USING_OFW') == False: if GetDepend('RT_USING_OFW') == False:
SrcRemove(src, ['ofw.c']) 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') Return('group')

View File

@ -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 <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);
}

View File

@ -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

View File

@ -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);

View File

@ -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 <rtthread.h>
#include <rtdevice.h>
#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

View File

@ -8,6 +8,11 @@
* 2024-09-25 zhujiale the first version * 2024-09-25 zhujiale the first version
*/ */
#include <rtthread.h> #include <rtthread.h>
#include <rtdevice.h>
#include <stdio.h>
#define DBG_TAG "rtdm.phy"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "ofw.h" #include "ofw.h"
static const char* const rt_phy_modes[] = 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; 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;
}

View File

@ -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(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_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); rt_err_t rt_ofw_get_interface(struct rt_ofw_node *np, rt_phy_interface *interface);
#endif #endif

View File

@ -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 * SPDX-License-Identifier: Apache-2.0
* *
* Change Logs: * Change Logs:
* Date Author Notes * Date Author Notes
* 2020-09-27 wangqiang first version * 2020-09-27 wangqiang first version
* 2024-10-08 zhujiale add phy v2.0
*/ */
#include <stddef.h> #include <stddef.h>
#include <rthw.h> #include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h> #include <rtdevice.h>
#define DBG_TAG "rtdm.phy"
#define DBG_TAG "PHY"
#define DBG_LVL DBG_INFO #define DBG_LVL DBG_INFO
#include <rtdbg.h> #include <rtdbg.h>
#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) 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; 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 PHYs identifier
* the register 3 contains the low 16 bits of the PHYs 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