Add a driver for the motorcomm yt8821 2.5G ethernet phy. Verified the driver on BPI-R3(with MediaTek MT7986(Filogic 830) SoC) development board, which is developed by Guangdong Bipai Technology Co., Ltd.. On the board, yt8821 2.5G ethernet phy works in AUTO_BX2500_SGMII or FORCE_BX2500 interface, supports 2.5G/1000M/100M/10M speeds, and wol(magic package). Since some functions of yt8821 are similar to YT8521 so some functions for yt8821 can be reused. Signed-off-by: Frank.Sae <Frank.Sae@xxxxxxxxxxxxxx> --- drivers/net/phy/motorcomm.c | 639 +++++++++++++++++++++++++++++++++++- 1 file changed, 636 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 7a11fdb687cc..a432b27dd849 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Motorcomm 8511/8521/8531/8531S PHY driver. + * Motorcomm 8511/8521/8531/8531S/8821 PHY driver. * * Author: Peter Geis <pgwipeout@xxxxxxxxx> * Author: Frank <Frank.Sae@xxxxxxxxxxxxxx> @@ -16,7 +16,7 @@ #define PHY_ID_YT8521 0x0000011a #define PHY_ID_YT8531 0x4f51e91b #define PHY_ID_YT8531S 0x4f51e91a - +#define PHY_ID_YT8821 0x4f51ea19 /* YT8521/YT8531S Register Overview * UTP Register space | FIBER Register space * ------------------------------------------------------------ @@ -52,6 +52,15 @@ #define YTPHY_SSR_SPEED_10M 0x0 #define YTPHY_SSR_SPEED_100M 0x1 #define YTPHY_SSR_SPEED_1000M 0x2 +/* bit9 as speed_mode[2], bit15:14 as Speed_mode[1:0] + * Speed_mode[2:0]: + * 100 = 2P5G + * 011 = 10G + * 010 = 1000 Mbps + * 001 = 100 Mbps + * 000 = 10 Mbps + */ +#define YT8821_SSR_SPEED_2500M 0x4 #define YTPHY_SSR_DUPLEX_OFFSET 13 #define YTPHY_SSR_DUPLEX BIT(13) #define YTPHY_SSR_PAGE_RECEIVED BIT(12) @@ -270,12 +279,59 @@ #define YT8531_SCR_CLK_SRC_REF_25M 4 #define YT8531_SCR_CLK_SRC_SSC_25M 5 +#define YT8821_SDS_EXT_CSR_CTRL_REG 0x23 +#define YT8821_SDS_EXT_CSR_PLL_SETTING 0x8605 +#define YT8821_UTP_EXT_FFE_IPR_CTRL_REG 0x34E +#define YT8821_UTP_EXT_FFE_SETTING 0x8080 +#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2 +#define YT8821_UTP_EXT_VGA_LPF1_CAP_SHT_SETTING 0x5200 +#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3 +#define YT8821_UTP_EXT_VGA_LPF2_CAP_SHT_SETTING 0x5200 +#define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372 +#define YT8821_UTP_EXT_TRACE_LNG_MED_GAIN_THR_SETTING 0x5A3C +#define YT8821_UTP_EXT_IPR_CTRL_REG 0x374 +#define YT8821_UTP_EXT_IPR_ALPHA_IPR_SETTING 0x7C6C +#define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336 +#define YT8821_UTP_EXT_ECHO_SETTING 0xAA0A +#define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340 +#define YT8821_UTP_EXT_AGC_MED_GAIN_SETTING 0x3022 +#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A +#define YT8821_UTP_EXT_TH_20DB_2500_SETTING 0x8000 +#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3 +#define YT8821_UTP_EXT_MU_COARSE_FR_FFE_GN_DC_SETTING 0x7711 +#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5 +#define YT8821_UTP_EXT_MU_FINE_FR_FFE_GN_DC_SETTING 0x2211 +#define YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG 0x56 +#define YT8821_UTP_EXT_ANALOG_CFG7_RESET 0x20 +#define YT8821_UTP_EXT_ANALOG_CFG7_PI_CLK_SEL_AFE 0x3F +#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97 +#define YT8821_UTP_EXT_VCT_CFG6_FECHO_AMP_TH_SETTING 0x380C +#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660 +#define YT8821_UTP_EXT_TXGE_NFR_FR_SETTING 0x112A +#define YT8821_UTP_EXT_PLL_CTRL_REG 0x450 +#define YT8821_UTP_EXT_PLL_SPARE_SETTING 0xE9 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_CTRL_REG 0x466 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL23_SETTING 0x6464 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_CTRL_REG 0x467 +#define YT8821_UTP_EXT_DAC_IMID_CHANNEL01_SETTING 0x6464 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_CTRL_REG 0x468 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_SETTING 0x6464 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_CTRL_REG 0x469 +#define YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_SETTING 0x6464 /* Extended Register end */ #define YTPHY_DTS_OUTPUT_CLK_DIS 0 #define YTPHY_DTS_OUTPUT_CLK_25M 25000000 #define YTPHY_DTS_OUTPUT_CLK_125M 125000000 +#define YT8821_CHIP_MODE_AUTO_BX2500_SGMII 0 +#define YT8821_CHIP_MODE_FORCE_BX2500 1 + +struct yt8821_priv { + /* chip mode: AUTO_BX2500_SGMII/FORCE_BX2500 */ + u32 chip_mode; +}; + struct yt8521_priv { /* combo_advertising is used for case of YT8521 in combo mode, * this means that yt8521 may work in utp or fiber mode which depends @@ -2252,6 +2308,564 @@ static int yt8521_get_features(struct phy_device *phydev) return ret; } +/** + * yt8821_probe() - read dts to get chip mode + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_probe(struct phy_device *phydev) +{ + struct device_node *node = phydev->mdio.dev.of_node; + struct device *dev = &phydev->mdio.dev; + struct yt8821_priv *priv; + u8 chip_mode; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + if (of_property_read_u8(node, "motorcomm,chip-mode", &chip_mode)) + chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; + + switch (chip_mode) { + case YT8821_CHIP_MODE_AUTO_BX2500_SGMII: + priv->chip_mode = YT8821_CHIP_MODE_AUTO_BX2500_SGMII; + break; + case YT8821_CHIP_MODE_FORCE_BX2500: + priv->chip_mode = YT8821_CHIP_MODE_FORCE_BX2500; + break; + default: + phydev_warn(phydev, "chip_mode err:%d\n", chip_mode); + return -EINVAL; + } + + return 0; +} + +/** + * yt8821_get_features - read mmd register to get 2.5G capability + * @phydev: target phy_device struct + * + * returns 0 or negative errno code + */ +static int yt8821_get_features(struct phy_device *phydev) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->supported, + val & MDIO_PMA_NG_EXTABLE_2_5GBT); + + return genphy_read_abilities(phydev); +} + +/** + * yt8821_get_rate_matching - read register to get phy chip mode + * @phydev: target phy_device struct + * @iface: PHY data interface type + * + * returns rate matching type or negative errno code + */ +static int yt8821_get_rate_matching(struct phy_device *phydev, + phy_interface_t iface) +{ + int val; + + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (val < 0) + return val; + + if ((val & YT8521_CCR_MODE_SEL_MASK) == + YT8821_CHIP_MODE_FORCE_BX2500) { + return RATE_MATCH_PAUSE; + } + + return RATE_MATCH_NONE; +} + +/** + * yt8821_aneg_done() - determines the auto negotiation result + * @phydev: a pointer to a &struct phy_device + * + * returns 0(no link)or 1(utp link) or negative errno code + */ +static int yt8821_aneg_done(struct phy_device *phydev) +{ + int link; + + link = yt8521_aneg_done_paged(phydev, YT8521_RSSR_UTP_SPACE); + + return link; +} + +/** + * yt8821gen_init_paged() - generic initialization according to page + * @phydev: a pointer to a &struct phy_device + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to + * operate. + * + * returns 0 or negative errno code + */ +static int yt8821gen_init_paged(struct phy_device *phydev, int page) +{ + int old_page; + int ret = 0; + + old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK); + if (old_page < 0) + goto err_restore_page; + + if (page & YT8521_RSSR_SPACE_MASK) { + /* sds init */ + ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, + YT8821_SDS_EXT_CSR_PLL_SETTING); + if (ret < 0) + goto err_restore_page; + } else { + /* utp init */ + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_FFE_IPR_CTRL_REG, + YT8821_UTP_EXT_FFE_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG, + YT8821_UTP_EXT_VGA_LPF1_CAP_SHT_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG, + YT8821_UTP_EXT_VGA_LPF2_CAP_SHT_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_TRACE_CTRL_REG, + YT8821_UTP_EXT_TRACE_LNG_MED_GAIN_THR_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_IPR_CTRL_REG, + YT8821_UTP_EXT_IPR_ALPHA_IPR_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_ECHO_CTRL_REG, + YT8821_UTP_EXT_ECHO_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_GAIN_CTRL_REG, + YT8821_UTP_EXT_AGC_MED_GAIN_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG, + YT8821_UTP_EXT_TH_20DB_2500_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG, + YT8821_UTP_EXT_MU_COARSE_FR_FFE_GN_DC_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG, + YT8821_UTP_EXT_MU_FINE_FR_FFE_GN_DC_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG, + YT8821_UTP_EXT_ANALOG_CFG7_RESET); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_ANALOG_CFG7_CTRL_REG, + YT8821_UTP_EXT_ANALOG_CFG7_PI_CLK_SEL_AFE); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_VCT_CFG6_CTRL_REG, + YT8821_UTP_EXT_VCT_CFG6_FECHO_AMP_TH_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG, + YT8821_UTP_EXT_TXGE_NFR_FR_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_PLL_CTRL_REG, + YT8821_UTP_EXT_PLL_SPARE_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CHANNEL23_CTRL_REG, + YT8821_UTP_EXT_DAC_IMID_CHANNEL23_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CHANNEL01_CTRL_REG, + YT8821_UTP_EXT_DAC_IMID_CHANNEL01_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_CTRL_REG, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL23_SETTING); + if (ret < 0) + goto err_restore_page; + + ret = ytphy_write_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_CTRL_REG, + YT8821_UTP_EXT_DAC_IMSB_CHANNEL01_SETTING); + if (ret < 0) + goto err_restore_page; + } + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8821gen_init() - generic initialization + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821gen_init(struct phy_device *phydev) +{ + int ret = 0; + + ret = yt8821gen_init_paged(phydev, YT8521_RSSR_FIBER_SPACE); + if (ret < 0) + return ret; + + return yt8821gen_init_paged(phydev, YT8521_RSSR_UTP_SPACE); +} + +/** + * yt8821_auto_sleep_config() - phy auto sleep config + * @phydev: a pointer to a &struct phy_device + * @enable: true enable auto sleep, false disable auto sleep + * + * returns 0 or negative errno code + */ +static int yt8821_auto_sleep_config(struct phy_device *phydev, bool enable) +{ + int old_page; + int ret = 0; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + ret = ytphy_modify_ext(phydev, + YT8521_EXTREG_SLEEP_CONTROL1_REG, + YT8521_ESC1R_SLEEP_SW, + enable ? 1 : 0); + if (ret < 0) + goto err_restore_page; + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8821_soft_reset() - soft reset utp and serdes + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_soft_reset(struct phy_device *phydev) +{ + return ytphy_modify_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG, + YT8521_CCR_SW_RST, 0); +} + +/** + * yt8821_config_init() - phy initializatioin + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_config_init(struct phy_device *phydev) +{ + struct yt8821_priv *priv = phydev->priv; + int ret, val; + + phydev->irq = PHY_POLL; + + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { + ret = ytphy_modify_ext_with_lock(phydev, + YT8521_CHIP_CONFIG_REG, + YT8521_CCR_MODE_SEL_MASK, + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 0)); + if (ret < 0) + return ret; + + __assign_bit(PHY_INTERFACE_MODE_2500BASEX, + phydev->possible_interfaces, + true); + __assign_bit(PHY_INTERFACE_MODE_SGMII, + phydev->possible_interfaces, + true); + + phydev->rate_matching = RATE_MATCH_NONE; + } else if (priv->chip_mode == YT8821_CHIP_MODE_FORCE_BX2500) { + ret = ytphy_modify_ext_with_lock(phydev, + YT8521_CHIP_CONFIG_REG, + YT8521_CCR_MODE_SEL_MASK, + FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, 1)); + if (ret < 0) + return ret; + + phydev->rate_matching = RATE_MATCH_PAUSE; + } + + ret = yt8821gen_init(phydev); + if (ret < 0) + return ret; + + /* disable auto sleep */ + ret = yt8821_auto_sleep_config(phydev, false); + if (ret < 0) + return ret; + + /* soft reset */ + yt8821_soft_reset(phydev); + + return ret; +} + +/** + * yt8821_adjust_status() - update speed and duplex to phydev + * @phydev: a pointer to a &struct phy_device + * @val: read from YTPHY_SPECIFIC_STATUS_REG + */ +static void yt8821_adjust_status(struct phy_device *phydev, int val) +{ + int speed_mode, duplex; + int speed_mode_low, speed_mode_high; + int speed = SPEED_UNKNOWN; + + duplex = FIELD_GET(YTPHY_SSR_DUPLEX, val); + + speed_mode_low = FIELD_GET(GENMASK(15, 14), val); + speed_mode_high = FIELD_GET(BIT(9), val); + speed_mode = FIELD_PREP(BIT(2), speed_mode_high) | + FIELD_PREP(GENMASK(1, 0), speed_mode_low); + switch (speed_mode) { + case YTPHY_SSR_SPEED_10M: + speed = SPEED_10; + break; + case YTPHY_SSR_SPEED_100M: + speed = SPEED_100; + break; + case YTPHY_SSR_SPEED_1000M: + speed = SPEED_1000; + break; + case YT8821_SSR_SPEED_2500M: + speed = SPEED_2500; + break; + default: + speed = SPEED_UNKNOWN; + break; + } + + phydev->speed = speed; + phydev->duplex = duplex; +} + +/** + * yt8821_update_interface() - update interface per current speed + * @phydev: a pointer to a &struct phy_device + */ +static void yt8821_update_interface(struct phy_device *phydev) +{ + if (!phydev->link) + return; + + switch (phydev->speed) { + case SPEED_2500: + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + break; + case SPEED_1000: + case SPEED_100: + case SPEED_10: + phydev->interface = PHY_INTERFACE_MODE_SGMII; + break; + default: + phydev_warn(phydev, "phy speed err:%d\n", phydev->speed); + break; + } +} + +/** + * yt8821_read_status() - determines the negotiated speed and duplex + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_read_status(struct phy_device *phydev) +{ + struct yt8821_priv *priv = phydev->priv; + int old_page; + int ret = 0; + int link; + int val; + + if (phydev->autoneg == AUTONEG_ENABLE) { + int lpadv = phy_read_mmd(phydev, + MDIO_MMD_AN, MDIO_AN_10GBT_STAT); + + if (lpadv < 0) + return lpadv; + + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, + lpadv); + } + + ret = ytphy_write_ext_with_lock(phydev, + YT8521_REG_SPACE_SELECT_REG, + YT8521_RSSR_UTP_SPACE); + if (ret < 0) + return ret; + + ret = genphy_read_status(phydev); + if (ret < 0) + return ret; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + + val = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); + if (val < 0) { + ret = val; + goto err_restore_page; + } + + link = val & YTPHY_SSR_LINK; + if (link) + yt8821_adjust_status(phydev, val); + + if (link) { + if (phydev->link == 0) + phydev_info(phydev, + "%s, phy addr: %d, link up, mii reg 0x%x = 0x%x\n", + __func__, phydev->mdio.addr, + YTPHY_SPECIFIC_STATUS_REG, + (unsigned int)val); + phydev->link = 1; + } else { + if (phydev->link == 1) + phydev_info(phydev, "%s, phy addr: %d, link down\n", + __func__, phydev->mdio.addr); + phydev->link = 0; + } + + if (priv->chip_mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) + yt8821_update_interface(phydev); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8821_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register + * @phydev: the phy_device struct + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * NOTE: Convenience function which allows a PHY's BMCR register to be + * modified as new register value = (old register value & ~mask) | set. + * + * returns 0 or negative errno code + */ +static int yt8821_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask, + u16 set) +{ + int ret; + + ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE, + mask, set); + if (ret < 0) + return ret; + + return yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE, + mask, set); +} + +/** + * yt8821_suspend() - suspend the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_suspend(struct phy_device *phydev) +{ + int wol_config; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8821_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN); +} + +/** + * yt8821_resume() - resume the hardware + * @phydev: a pointer to a &struct phy_device + * + * returns 0 or negative errno code + */ +static int yt8821_resume(struct phy_device *phydev) +{ + int wol_config; + int ret; + + /* disable auto sleep */ + ret = yt8821_auto_sleep_config(phydev, false); + if (ret < 0) + return ret; + + wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG); + if (wol_config < 0) + return wol_config; + + /* if wol enable, do nothing */ + if (wol_config & YTPHY_WCR_ENABLE) + return 0; + + return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0); +} + static struct phy_driver motorcomm_phy_drvs[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8511), @@ -2307,11 +2921,29 @@ static struct phy_driver motorcomm_phy_drvs[] = { .suspend = yt8521_suspend, .resume = yt8521_resume, }, + { + 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, + .set_wol = ytphy_set_wol, + .config_aneg = genphy_config_aneg, + .aneg_done = yt8821_aneg_done, + .config_init = yt8821_config_init, + .get_rate_matching = yt8821_get_rate_matching, + .read_status = yt8821_read_status, + .soft_reset = yt8821_soft_reset, + .suspend = yt8821_suspend, + .resume = yt8821_resume, + }, }; module_phy_driver(motorcomm_phy_drvs); -MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver"); +MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821 PHY driver"); MODULE_AUTHOR("Peter Geis"); MODULE_AUTHOR("Frank"); MODULE_LICENSE("GPL"); @@ -2321,6 +2953,7 @@ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) }, { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8821) }, { /* sentinel */ } }; -- 2.25.1