diff --git a/components/drivers/phy/Kconfig b/components/drivers/phy/Kconfig index cf0560aed5..805ab602d1 100644 --- a/components/drivers/phy/Kconfig +++ b/components/drivers/phy/Kconfig @@ -8,5 +8,8 @@ menuconfig RT_USING_PHY_V2 default n if RT_USING_DM && RT_USING_PHY_V2 + config RT_PHY_V2_REALTEK + bool "Realtek" + osource "$(SOC_DM_PHY_DIR)/Kconfig" endif diff --git a/components/drivers/phy/SConscript b/components/drivers/phy/SConscript index ba227c72d4..2bf716b8a7 100644 --- a/components/drivers/phy/SConscript +++ b/components/drivers/phy/SConscript @@ -1,17 +1,23 @@ from building import * -cwd = GetCurrentDir() +group = [] + +if not GetDepend(['RT_USING_PHY']) and not GetDepend(['RT_USING_PHY_V2']): + Return('group') + +cwd = GetCurrentDir() CPPPATH = [cwd, cwd + '/../include'] -src = Glob('*.c') -if GetDepend('RT_USING_OFW') == False: - SrcRemove(src, ['ofw.c']) -if GetDepend('RT_USING_PHY_V2') == False: - SrcRemove(src, ['general.c','mdio.c','ofw.c']) +src = ['phy.c'] -if GetDepend('RT_USING_PHY_V2') == False: - if GetDepend('RT_USING_PHY') == False: - SrcRemove(src, ['phy.c']) +if GetDepend(['RT_USING_PHY_V2']): + src += ['general.c', 'mdio.c'] + + if GetDepend(['RT_USING_OFW']): + src += ['ofw.c'] + + if GetDepend(['RT_PHY_V2_REALTEK']): + src += ['phy-realtek.c'] group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) diff --git a/components/drivers/phy/general.c b/components/drivers/phy/general.c index a75d76468a..23aca4abe6 100644 --- a/components/drivers/phy/general.c +++ b/components/drivers/phy/general.c @@ -120,6 +120,7 @@ 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; @@ -161,43 +162,40 @@ int rt_genphy_config_aneg(struct rt_phy_device *phydev) int rt_genphy_update_link(struct rt_phy_device *phydev) { - unsigned int mii_reg; + int bmcr, mii_reg; - mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + bmcr = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMCR); + if (bmcr < 0) + return bmcr; - if (phydev->link && mii_reg & RT_BMSR_LSTATUS) - return 0; - - if ((phydev->autoneg == AUTONEG_ENABLE) && - !(mii_reg & RT_BMSR_ANEGCOMPLETE)) - { - int i = 0; - LOG_I("Waiting for PHY auto negotiation to complete"); - while (!(mii_reg & RT_BMSR_ANEGCOMPLETE)) - { - - if (i > (RT_PHY_ANEG_TIMEOUT)) - { - LOG_E(" TIMEOUT!"); - phydev->link = 0; - return -ETIMEDOUT; - } - - mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); - - rt_thread_delay(100); - i += 100; - } - LOG_D(" Done"); - 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; + if (bmcr & RT_BMCR_ANRESTART) + { + mii_reg = 0; } + else + { + /* + * BMSR link status is latched low. Follow Linux genphy_update_link() + * and read it twice when the link was previously down. + */ + if (!phydev->link) + { + mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + if (mii_reg < 0) + return mii_reg; + if (mii_reg & RT_BMSR_LSTATUS) + goto _done; + } + + mii_reg = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RT_MII_BMSR); + if (mii_reg < 0) + return mii_reg; + } + +_done: + phydev->link = (mii_reg & RT_BMSR_LSTATUS) ? 1 : 0; + if (phydev->autoneg == AUTONEG_ENABLE && !(mii_reg & RT_BMSR_ANEGCOMPLETE)) + phydev->link = 0; return 0; } @@ -343,8 +341,10 @@ int rt_genphy_startup(struct rt_phy_device *phydev) int ret; ret = rt_genphy_update_link(phydev); - if (ret) + if (ret < 0) return ret; - return rt_genphy_parse_link(phydev); + ret = rt_genphy_parse_link(phydev); + + return ret; } diff --git a/components/drivers/phy/mdio.c b/components/drivers/phy/mdio.c index 7f9be35478..0c2706c16d 100644 --- a/components/drivers/phy/mdio.c +++ b/components/drivers/phy/mdio.c @@ -35,7 +35,8 @@ struct mii_bus *rt_mdio_get_bus_by_name(const char *busname) struct mii_bus *rt_mdio_alloc(void) { struct mii_bus *mii; - mii = rt_malloc(sizeof(struct mii_bus)); + + mii = rt_calloc(1, sizeof(*mii)); if (!mii) return RT_NULL; diff --git a/components/drivers/phy/ofw.c b/components/drivers/phy/ofw.c index 933255e822..0eaaf9d727 100644 --- a/components/drivers/phy/ofw.c +++ b/components/drivers/phy/ofw.c @@ -13,6 +13,7 @@ #define DBG_TAG "rtdm.phy" #define DBG_LVL DBG_LOG #include +#include "mdio.h" #include "ofw.h" static const char* const rt_phy_modes[] = @@ -108,22 +109,45 @@ rt_err_t rt_ofw_get_mac_addr(struct rt_ofw_node *np, rt_uint8_t *addr) rt_err_t rt_ofw_get_phyid(struct rt_ofw_node *np,rt_uint32_t *id) { + struct rt_ofw_prop *prop; 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 = rt_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; + rt_ofw_foreach_prop_string(np, "compatible", prop, phy_id) + { + if (rt_sscanf(phy_id, "ethernet-phy-id%4x.%4x", &upper, &lower) == 2) + { + *id = ((upper & 0xffff) << 16) | (lower & 0xffff); + return RT_EOK; + } + } + return -RT_ERROR; } + +static int ofw_read_phy_id(struct mii_bus *bus, int addr, rt_uint32_t *id) +{ + int reg; + + reg = bus->read(bus, addr, RT_MDIO_DEVAD_NONE, RT_MII_PHYSID1); + if (reg < 0) + { + return -RT_EIO; + } + + *id = (reg & 0xffff) << 16; + + reg = bus->read(bus, addr, RT_MDIO_DEVAD_NONE, RT_MII_PHYSID2); + if (reg < 0) + { + return -RT_EIO; + } + + *id |= (reg & 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; @@ -138,19 +162,57 @@ struct rt_phy_device *rt_ofw_create_phy(struct mii_bus *bus,struct rt_ofw_node * return RT_NULL; } - ret = rt_ofw_get_phyid(np, &id); + ret = rt_ofw_get_phyid(phy_node, &id); if (ret) { - LOG_D("Failed to read eth PHY id, err: %d\n", ret); - return RT_NULL; + rt_uint32_t reg = 0; + int addr; + + if (rt_ofw_prop_read_u32(phy_node, "reg", ®)) + { + rt_ofw_node_put(phy_node); + return RT_NULL; + } + + addr = (phyaddr >= 0) ? phyaddr : (int)reg; + id = 0xffffffff; + + if (!ofw_read_phy_id(bus, addr, &id) && + (id & 0x1fffffff) != 0x1fffffff) + { + LOG_D("PHY id from MDIO: 0x%08x @ addr %d", id, addr); + } + else + { + id = 0xffffffff; + LOG_D("MDIO PHY id read failed @ addr %d, use generic PHY", addr); + } + + dev = rt_phy_device_create(bus, addr, id, RT_FALSE); + if (dev) + { + dev->node = phy_node; + } + else + { + rt_ofw_node_put(phy_node); + } + + return dev; } - LOG_D("Found a PHY id: 0x%x\n", id); + LOG_D("Found a PHY id: 0x%x", id); dev = rt_phy_device_create(bus, phyaddr, id, RT_FALSE); - if(dev) + if (dev) + { dev->node = phy_node; + } + else + { + rt_ofw_node_put(phy_node); + } return dev; } diff --git a/components/drivers/phy/phy-realtek.c b/components/drivers/phy/phy-realtek.c new file mode 100644 index 0000000000..01d081215f --- /dev/null +++ b/components/drivers/phy/phy-realtek.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "rtdm.phy.rtl" +#define DBG_LVL DBG_INFO +#include + +#define RTL8211F_PHY_ID 0x001cc916 +#define RTL8211FVD_PHY_ID 0x001cc878 +#define RTL8211F_PHY_ID_MASK 0x001fffff + +#define RTL8211F_PAGE_SELECT 0x1f + +#define RTL8211F_PHYCR_PAGE 0x0a43 +#define RTL8211F_PHYCR1 0x18 +#define RTL8211F_PHYCR2 0x19 + +#define RTL8211F_RGMII_PAGE 0x0d08 +#define RTL8211F_TXCR 0x11 +#define RTL8211F_RXCR 0x15 +#define RTL8211F_TX_DELAY RT_BIT(8) +#define RTL8211F_RX_DELAY RT_BIT(3) + +#define RTL8211F_ALDPS_PLL_OFF RT_BIT(1) +#define RTL8211F_ALDPS_ENABLE RT_BIT(2) +#define RTL8211F_ALDPS_XTAL_OFF RT_BIT(12) +#define RTL8211F_CLKOUT_EN RT_BIT(0) + +struct rtl821x_priv +{ + rt_uint16_t phycr1; + rt_uint16_t phycr2; + rt_bool_t has_phycr2; +}; + +static int rtl8211f_read_page(struct rt_phy_device *phydev) +{ + return rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, RTL8211F_PAGE_SELECT); +} + +static int rtl8211f_write_page(struct rt_phy_device *phydev, int page) +{ + return rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, RTL8211F_PAGE_SELECT, page); +} + +static int rtl8211f_read_paged(struct rt_phy_device *phydev, int page, int reg) +{ + int oldpage, val; + + oldpage = rtl8211f_read_page(phydev); + if (oldpage < 0) + return oldpage; + + val = rtl8211f_write_page(phydev, page); + if (val < 0) + return val; + + val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, reg); + rtl8211f_write_page(phydev, oldpage); + + return val; +} + +static int rtl8211f_modify_paged(struct rt_phy_device *phydev, int page, + int reg, rt_uint16_t mask, rt_uint16_t set) +{ + int oldpage, val, ret; + + oldpage = rtl8211f_read_page(phydev); + if (oldpage < 0) + return oldpage; + + ret = rtl8211f_write_page(phydev, page); + if (ret < 0) + return ret; + + val = rt_phy_read(phydev, RT_MDIO_DEVAD_NONE, reg); + if (val < 0) + { + rtl8211f_write_page(phydev, oldpage); + return val; + } + + val &= ~mask; + val |= set & mask; + + ret = rt_phy_write(phydev, RT_MDIO_DEVAD_NONE, reg, val); + rtl8211f_write_page(phydev, oldpage); + + return ret; +} + +static int rtl8211f_config_delay(struct rt_phy_device *phydev) +{ + rt_uint16_t tx_delay = 0; + rt_uint16_t rx_delay = 0; + int ret; + + switch (phydev->interface) + { + case RT_PHY_INTERFACE_MODE_RGMII: + break; + case RT_PHY_INTERFACE_MODE_RGMII_RXID: + rx_delay = RTL8211F_RX_DELAY; + break; + case RT_PHY_INTERFACE_MODE_RGMII_TXID: + tx_delay = RTL8211F_TX_DELAY; + break; + case RT_PHY_INTERFACE_MODE_RGMII_ID: + tx_delay = RTL8211F_TX_DELAY; + rx_delay = RTL8211F_RX_DELAY; + break; + default: + return 0; + } + + ret = rtl8211f_modify_paged(phydev, RTL8211F_RGMII_PAGE, RTL8211F_TXCR, + RTL8211F_TX_DELAY, tx_delay); + if (ret < 0) + return ret; + + return rtl8211f_modify_paged(phydev, RTL8211F_RGMII_PAGE, RTL8211F_RXCR, + RTL8211F_RX_DELAY, rx_delay); +} + +static rt_bool_t rtl8211f_ofw_bool(struct rt_phy_device *phydev, + const char *propname) +{ +#ifdef RT_USING_OFW + if (phydev->node) + return rt_ofw_prop_read_bool(phydev->node, propname); +#else + RT_UNUSED(phydev); + RT_UNUSED(propname); +#endif + + return RT_FALSE; +} + +static int rtl8211f_probe(struct rt_phy_device *phydev) +{ + struct rtl821x_priv *priv; + int val; + + priv = rt_calloc(1, sizeof(*priv)); + if (!priv) + return -RT_ENOMEM; + + val = rtl8211f_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1); + if (val < 0) + { + rt_free(priv); + return val; + } + + priv->phycr1 = val & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | + RTL8211F_ALDPS_XTAL_OFF); + if (rtl8211f_ofw_bool(phydev, "realtek,aldps-enable")) + { + priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | + RTL8211F_ALDPS_XTAL_OFF; + } + + priv->has_phycr2 = phydev->phy_id != RTL8211FVD_PHY_ID; + if (priv->has_phycr2) + { + val = rtl8211f_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2); + if (val < 0) + { + rt_free(priv); + return val; + } + + priv->phycr2 = val & RTL8211F_CLKOUT_EN; + if (rtl8211f_ofw_bool(phydev, "realtek,clkout-disable")) + priv->phycr2 &= ~RTL8211F_CLKOUT_EN; + } + + phydev->priv = priv; + + return 0; +} + +static int rtl8211f_config_init(struct rt_phy_device *phydev) +{ + struct rtl821x_priv *priv = phydev->priv; + int ret; + + if (!priv) + return -RT_EINVAL; + + ret = rtl8211f_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1, + RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | + RTL8211F_ALDPS_XTAL_OFF, priv->phycr1); + if (ret < 0) + return ret; + + ret = rtl8211f_config_delay(phydev); + if (ret < 0) + return ret; + + if (priv->has_phycr2) + { + ret = rtl8211f_modify_paged(phydev, RTL8211F_PHYCR_PAGE, + RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, priv->phycr2); + if (ret < 0) + return ret; + + return rt_phy_reset(phydev); + } + + return 0; +} + +static int rtl8211f_config(struct rt_phy_device *phydev) +{ + int ret; + + ret = rtl8211f_config_init(phydev); + if (ret < 0) + return ret; + + return rt_genphy_config(phydev); +} + +static int rtl8211f_startup(struct rt_phy_device *phydev) +{ + int ret; + + ret = rt_genphy_startup(phydev); + if (ret < 0 || !phydev->link) + { + if (ret < 0) + return ret; + + return -ETIMEDOUT; + } + + return 0; +} + +static int rtl8211f_shutdown(struct rt_phy_device *phydev) +{ + if (phydev->priv) + { + rt_free(phydev->priv); + phydev->priv = RT_NULL; + } + + return 0; +} + +static struct rt_phy_driver rtl8211f_driver = +{ + .uid = RTL8211F_PHY_ID, + .mask = RTL8211F_PHY_ID_MASK, + .name = "RTL8211F", + .features = RT_PHY_GBIT_FEATURES, + .probe = rtl8211f_probe, + .config = rtl8211f_config, + .startup = rtl8211f_startup, + .shutdown = rtl8211f_shutdown, +}; +RT_PHY_DRIVER_REGISTER(rtl8211f_driver); diff --git a/components/drivers/phy/phy.c b/components/drivers/phy/phy.c index 47c35c2103..232f09a8b1 100644 --- a/components/drivers/phy/phy.c +++ b/components/drivers/phy/phy.c @@ -379,6 +379,7 @@ static struct rt_phy_device *create_phy_by_mask(struct mii_bus *bus, unsigned in return rt_phy_device_create(bus, addr, id, is_c45); } + phy_mask &= ~(1U << addr); } return RT_NULL; } @@ -512,7 +513,11 @@ rt_err_t rt_phy_device_register(struct rt_phy_device *pdev) return err; } if(!pdev->drv) + { pdev->drv = &genphy; + pdev->advertising = pdev->drv->features; + pdev->supported = pdev->drv->features; + } return RT_EOK; }