diff options
author | Frank Sae <Frank.Sae@motor-comm.com> | 2024-09-01 01:35:26 -0700 |
---|---|---|
committer | Paolo Abeni <pabeni@redhat.com> | 2024-09-05 15:21:12 +0200 |
commit | b671105b88c3bb9acc1fb61a3ee2ca0ece60cb8d (patch) | |
tree | 36f23e46903ea6f79cc0bbf6d30a241176f83472 /drivers/net/phy | |
parent | 8d878c87b5c45ae64b0aecd4aac71e210d19173f (diff) |
net: phy: Add driver for Motorcomm yt8821 2.5G ethernet phy
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..
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).
Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
Reviewed-by: Sai Krishna <saikrishnag@marvell.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/motorcomm.c | 671 |
1 files changed, 667 insertions, 4 deletions
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index fe0aabe12622..0e91f5d1a4fd 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@gmail.com> * Author: Frank <Frank.Sae@motor-comm.com> @@ -16,8 +16,8 @@ #define PHY_ID_YT8521 0x0000011a #define PHY_ID_YT8531 0x4f51e91b #define PHY_ID_YT8531S 0x4f51e91a - -/* YT8521/YT8531S Register Overview +#define PHY_ID_YT8821 0x4f51ea19 +/* YT8521/YT8531S/YT8821 Register Overview * UTP Register space | FIBER Register space * ------------------------------------------------------------ * | UTP MII | FIBER MII | @@ -50,6 +50,8 @@ #define YTPHY_SSR_SPEED_10M ((0x0 << 14)) #define YTPHY_SSR_SPEED_100M ((0x1 << 14)) #define YTPHY_SSR_SPEED_1000M ((0x2 << 14)) +#define YTPHY_SSR_SPEED_10G ((0x3 << 14)) +#define YTPHY_SSR_SPEED_2500M ((0x0 << 14) | BIT(9)) #define YTPHY_SSR_DUPLEX_OFFSET 13 #define YTPHY_SSR_DUPLEX BIT(13) #define YTPHY_SSR_PAGE_RECEIVED BIT(12) @@ -268,12 +270,89 @@ #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_VCO_LDO_EN BIT(15) +#define YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN BIT(8) + +#define YT8821_UTP_EXT_PI_CTRL_REG 0x56 +#define YT8821_UTP_EXT_PI_RST_N_FIFO BIT(5) +#define YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE BIT(4) +#define YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE BIT(3) +#define YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE BIT(2) +#define YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE BIT(1) +#define YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE BIT(0) + +#define YT8821_UTP_EXT_VCT_CFG6_CTRL_REG 0x97 +#define YT8821_UTP_EXT_FECHO_AMP_TH_HUGE GENMASK(15, 8) + +#define YT8821_UTP_EXT_ECHO_CTRL_REG 0x336 +#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000 GENMASK(14, 8) + +#define YT8821_UTP_EXT_GAIN_CTRL_REG 0x340 +#define YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000 GENMASK(6, 0) + +#define YT8821_UTP_EXT_RPDN_CTRL_REG 0x34E +#define YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 BIT(15) +#define YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 BIT(7) +#define YT8821_UTP_EXT_RPDN_IPR_SHT_2500 GENMASK(6, 0) + +#define YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG 0x36A +#define YT8821_UTP_EXT_TH_20DB_2500 GENMASK(15, 0) + +#define YT8821_UTP_EXT_TRACE_CTRL_REG 0x372 +#define YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 GENMASK(14, 8) +#define YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500 GENMASK(6, 0) + +#define YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG 0x374 +#define YT8821_UTP_EXT_ALPHA_SHT_2500 GENMASK(14, 8) +#define YT8821_UTP_EXT_IPR_LNG_2500 GENMASK(6, 0) + +#define YT8821_UTP_EXT_PLL_CTRL_REG 0x450 +#define YT8821_UTP_EXT_PLL_SPARE_CFG GENMASK(7, 0) + +#define YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG 0x466 +#define YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG 0x467 +#define YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG 0x468 +#define YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG 0x469 +#define YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG GENMASK(14, 8) +#define YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG GENMASK(6, 0) + +#define YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG 0x4B3 +#define YT8821_UTP_EXT_MU_COARSE_FR_F_FFE GENMASK(14, 12) +#define YT8821_UTP_EXT_MU_COARSE_FR_F_FBE GENMASK(10, 8) + +#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5 +#define YT8821_UTP_EXT_MU_FINE_FR_F_FFE GENMASK(14, 12) +#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8) + +#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2 +#define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER GENMASK(7, 4) +#define YT8821_UTP_EXT_VGA_LPF1_CAP_2500 GENMASK(3, 0) + +#define YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG 0x4D3 +#define YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER GENMASK(7, 4) +#define YT8821_UTP_EXT_VGA_LPF2_CAP_2500 GENMASK(3, 0) + +#define YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG 0x660 +#define YT8821_UTP_EXT_NFR_TX_ABILITY BIT(3) /* 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 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 @@ -2249,6 +2328,572 @@ static int yt8521_get_features(struct phy_device *phydev) return ret; } +/** + * 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 ret; + + ret = genphy_c45_pma_read_ext_abilities(phydev); + if (ret < 0) + return ret; + + 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 (FIELD_GET(YT8521_CCR_MODE_SEL_MASK, val) == + 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) +{ + return yt8521_aneg_done_paged(phydev, YT8521_RSSR_UTP_SPACE); +} + +/** + * yt8821_serdes_init() - serdes init + * @phydev: a pointer to a &struct phy_device + * + * Returns: 0 or negative errno code + */ +static int yt8821_serdes_init(struct phy_device *phydev) +{ + int old_page; + int ret = 0; + u16 mask; + u16 set; + + old_page = phy_select_page(phydev, YT8521_RSSR_FIBER_SPACE); + if (old_page < 0) { + phydev_err(phydev, "Failed to select page: %d\n", + old_page); + goto err_restore_page; + } + + ret = __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_SDS_EXT_CSR_VCO_LDO_EN | + YT8821_SDS_EXT_CSR_VCO_BIAS_LPF_EN; + set = YT8821_SDS_EXT_CSR_VCO_LDO_EN; + ret = ytphy_modify_ext(phydev, YT8821_SDS_EXT_CSR_CTRL_REG, mask, + set); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * yt8821_utp_init() - utp init + * @phydev: a pointer to a &struct phy_device + * + * Returns: 0 or negative errno code + */ +static int yt8821_utp_init(struct phy_device *phydev) +{ + int old_page; + int ret = 0; + u16 mask; + u16 save; + u16 set; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) { + phydev_err(phydev, "Failed to select page: %d\n", + old_page); + goto err_restore_page; + } + + mask = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 | + YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500 | + YT8821_UTP_EXT_RPDN_IPR_SHT_2500; + set = YT8821_UTP_EXT_RPDN_BP_FFE_LNG_2500 | + YT8821_UTP_EXT_RPDN_BP_FFE_SHT_2500; + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_RPDN_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER | + YT8821_UTP_EXT_VGA_LPF1_CAP_2500; + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG, + mask, 0); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_VGA_LPF2_CAP_OTHER | + YT8821_UTP_EXT_VGA_LPF2_CAP_2500; + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_VGA_LPF2_CAP_CTRL_REG, + mask, 0); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500 | + YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500; + set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THE_2500, 0x5a) | + FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THE_2500, 0x3c); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_TRACE_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_IPR_LNG_2500; + set = FIELD_PREP(YT8821_UTP_EXT_IPR_LNG_2500, 0x6c); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_ALPHA_IPR_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000; + set = FIELD_PREP(YT8821_UTP_EXT_TRACE_LNG_GAIN_THR_1000, 0x2a); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_ECHO_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000; + set = FIELD_PREP(YT8821_UTP_EXT_TRACE_MED_GAIN_THR_1000, 0x22); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_GAIN_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_TH_20DB_2500; + set = FIELD_PREP(YT8821_UTP_EXT_TH_20DB_2500, 0x8000); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_TH_20DB_2500_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_MU_COARSE_FR_F_FFE | + YT8821_UTP_EXT_MU_COARSE_FR_F_FBE; + set = FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FFE, 0x7) | + FIELD_PREP(YT8821_UTP_EXT_MU_COARSE_FR_F_FBE, 0x7); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_MU_COARSE_FR_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_MU_FINE_FR_F_FFE | + YT8821_UTP_EXT_MU_FINE_FR_F_FBE; + set = FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FFE, 0x2) | + FIELD_PREP(YT8821_UTP_EXT_MU_FINE_FR_F_FBE, 0x2); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + /* save YT8821_UTP_EXT_PI_CTRL_REG's val for use later */ + ret = ytphy_read_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG); + if (ret < 0) + goto err_restore_page; + + save = ret; + + mask = YT8821_UTP_EXT_PI_TX_CLK_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_3_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_2_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_1_SEL_AFE | + YT8821_UTP_EXT_PI_RX_CLK_0_SEL_AFE; + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG, + mask, 0); + if (ret < 0) + goto err_restore_page; + + /* restore YT8821_UTP_EXT_PI_CTRL_REG's val */ + ret = ytphy_write_ext(phydev, YT8821_UTP_EXT_PI_CTRL_REG, save); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_FECHO_AMP_TH_HUGE; + set = FIELD_PREP(YT8821_UTP_EXT_FECHO_AMP_TH_HUGE, 0x38); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_VCT_CFG6_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_NFR_TX_ABILITY; + set = YT8821_UTP_EXT_NFR_TX_ABILITY; + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_TXGE_NFR_FR_THP_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_PLL_SPARE_CFG; + set = FIELD_PREP(YT8821_UTP_EXT_PLL_SPARE_CFG, 0xe9); + ret = ytphy_modify_ext(phydev, YT8821_UTP_EXT_PLL_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG | + YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_3_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_2_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CH_2_3_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG | + YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_1_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMID_CH_0_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMID_CH_0_1_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG | + YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_3_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_2_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CH_2_3_CTRL_REG, + mask, set); + if (ret < 0) + goto err_restore_page; + + mask = YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG | + YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG; + set = FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_1_10_ORG, 0x64) | + FIELD_PREP(YT8821_UTP_EXT_DAC_IMSB_CH_0_10_ORG, 0x64); + ret = ytphy_modify_ext(phydev, + YT8821_UTP_EXT_DAC_IMSB_CH_0_1_CTRL_REG, + mask, set); + +err_restore_page: + return phy_restore_page(phydev, old_page, ret); +} + +/** + * 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) { + phydev_err(phydev, "Failed to select page: %d\n", + old_page); + goto err_restore_page; + } + + ret = ytphy_modify_ext(phydev, + YT8521_EXTREG_SLEEP_CONTROL1_REG, + YT8521_ESC1R_SLEEP_SW, + enable ? 1 : 0); + +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) +{ + u8 mode = YT8821_CHIP_MODE_AUTO_BX2500_SGMII; + int ret; + u16 set; + + if (phydev->interface == PHY_INTERFACE_MODE_2500BASEX) + mode = YT8821_CHIP_MODE_FORCE_BX2500; + + set = FIELD_PREP(YT8521_CCR_MODE_SEL_MASK, mode); + ret = ytphy_modify_ext_with_lock(phydev, + YT8521_CHIP_CONFIG_REG, + YT8521_CCR_MODE_SEL_MASK, + set); + if (ret < 0) + return ret; + + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + phydev->possible_interfaces); + + if (mode == YT8821_CHIP_MODE_AUTO_BX2500_SGMII) { + __set_bit(PHY_INTERFACE_MODE_SGMII, + phydev->possible_interfaces); + + phydev->rate_matching = RATE_MATCH_NONE; + } else if (mode == YT8821_CHIP_MODE_FORCE_BX2500) { + phydev->rate_matching = RATE_MATCH_PAUSE; + } + + ret = yt8821_serdes_init(phydev); + if (ret < 0) + return ret; + + ret = yt8821_utp_init(phydev); + if (ret < 0) + return ret; + + /* disable auto sleep */ + ret = yt8821_auto_sleep_config(phydev, false); + if (ret < 0) + return ret; + + /* soft reset */ + return yt8821_soft_reset(phydev); +} + +/** + * 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, duplex; + int speed_mode; + + duplex = FIELD_GET(YTPHY_SSR_DUPLEX, val); + speed_mode = val & YTPHY_SSR_SPEED_MASK; + 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 YTPHY_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) +{ + int link; + int ret; + int val; + + 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; + + if (phydev->autoneg_complete) { + ret = genphy_c45_read_lpa(phydev); + if (ret < 0) + return ret; + } + + ret = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); + if (ret < 0) + return ret; + + val = ret; + + link = val & YTPHY_SSR_LINK; + if (link) + yt8821_adjust_status(phydev, val); + + if (link) { + if (phydev->link == 0) + phydev_dbg(phydev, + "%s, phy addr: %d, link up\n", + __func__, phydev->mdio.addr); + phydev->link = 1; + } else { + if (phydev->link == 1) + phydev_dbg(phydev, + "%s, phy addr: %d, link down\n", + __func__, phydev->mdio.addr); + phydev->link = 0; + } + + val = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (val < 0) + return val; + + if (FIELD_GET(YT8521_CCR_MODE_SEL_MASK, val) == + YT8821_CHIP_MODE_AUTO_BX2500_SGMII) + yt8821_update_interface(phydev); + + return 0; +} + +/** + * 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), @@ -2304,11 +2949,28 @@ 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, + .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"); @@ -2318,6 +2980,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 */ } }; |