The YT8821 PHY responds on two MDIO addresses by default: the address selected by its strapping pins and the broadcast address 0. On platforms where another PHY is hardwired to respond only on address 0 (e.g. the internal Gigabit PHY in the MediaTek MT7981B SoC), this can lead to MDIO bus conflicts. The YT8821 may incorrectly respond to transactions intended for the other PHY, leaving it in an inconsistent state. The following issues were observed on a Cudy M3000 router: - Achieving just 100 Mbps speeds on gigabit links. Dmesg would show messages like [ 133.997177] YT8821 2.5Gbps PHY mdio-bus:01: Downshift occurred from negotiated speed 1Gbps to actual speed 100Mbps, check cabling! [ 134.009400] mtk_soc_eth 15100000.ethernet eth0: Link is Up - 100Mbps/Full - flow control off - Having the PHY report that the link is up, yet no data would flow. - The YT8821 would be affected by an "ip link set dev eth1 down" command aimed at the other PHY. Avoid this conflict by disabling the broadcast address 0 early in the PHY configuration process. The .probe callback is called early enough to stop the issues from occurring. The .config_init callback re-establishes the workaround after the PHY undergoes a hardware reset. This change makes the YT8821 work reliably on the Cudy M3000 router when running the current OpenWrt main branch with a small device tree patch. Link: https://github.com/openwrt/openwrt/pull/21584 Fixes: b671105b88c3 ("net: phy: Add driver for Motorcomm yt8821 2.5G ethernet phy") Signed-off-by: Jakub Vaněk --- drivers/net/phy/motorcomm.c | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 42d46b5758fc..bcb60d0bcab5 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -227,6 +227,9 @@ #define YT8521_LED_100_ON_EN BIT(5) #define YT8521_LED_10_ON_EN BIT(4) +#define YTPHY_MDIO_ADDRESS_CONTROL_REG 0xA005 +#define YTPHY_MACR_EN_PHY_ADDR_0 BIT(6) + #define YTPHY_MISC_CONFIG_REG 0xA006 #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) #define YTPHY_MCR_FIBER_1000BX (0x1 << 0) @@ -2760,6 +2763,30 @@ static int yt8821_soft_reset(struct phy_device *phydev) YT8521_CCR_SW_RST, 0); } +/** + * yt8821_disable_mdio_address_zero() - disable MDIO broadcast address 0 + * @phydev: a pointer to a &struct phy_device + * + * The YT8821 responds on two MDIO addresses by default: + * - the address selected by its strapping pins + * - the broadcast address 0 + * + * Some other PHYs (e.g. the MT7981B internal Gigabit PHY) are hardwired to + * respond only on MDIO address 0. If the YT8821 also listens on address 0, + * it may incorrectly react to transactions intended for those PHYs. + * + * Disabling address 0 on the YT8821 early avoids such MDIO bus conflicts. + * + * Returns: 0 or negative errno code + */ +static int yt8821_disable_mdio_address_zero(struct phy_device *phydev) +{ + return ytphy_modify_ext(phydev, + YTPHY_MDIO_ADDRESS_CONTROL_REG, + YTPHY_MACR_EN_PHY_ADDR_0, + 0); +} + /** * yt8821_config_init() - phy initializatioin * @phydev: a pointer to a &struct phy_device @@ -2795,6 +2822,10 @@ static int yt8821_config_init(struct phy_device *phydev) phydev->rate_matching = RATE_MATCH_PAUSE; } + ret = yt8821_disable_mdio_address_zero(phydev); + if (ret < 0) + return ret; + ret = yt8821_serdes_init(phydev); if (ret < 0) return ret; @@ -2812,6 +2843,22 @@ static int yt8821_config_init(struct phy_device *phydev) return yt8821_soft_reset(phydev); } +/** + * yt8821_probe() - early PHY initialization + * @phydev: a pointer to a &struct phy_device + * + * Returns: 0 or negative errno code + */ +static int yt8821_probe(struct phy_device *phydev) +{ + /* + * Disable the MDIO broadcast address (0) as early as possible. + * This prevents the YT8821 from responding to transactions + * intended for a different PHY that is fixed at address 0. + */ + return yt8821_disable_mdio_address_zero(phydev); +} + /** * yt8821_adjust_status() - update speed and duplex to phydev * @phydev: a pointer to a &struct phy_device @@ -3073,6 +3120,7 @@ static struct phy_driver motorcomm_phy_drvs[] = { PHY_ID_MATCH_EXACT(PHY_ID_YT8821), .name = "YT8821 2.5Gbps PHY", .get_features = yt8821_get_features, + .probe = yt8821_probe, .read_page = yt8521_read_page, .write_page = yt8521_write_page, .get_wol = ytphy_get_wol, -- 2.43.0