diff --git a/target/linux/generic/backport-6.6/750-01-v6.8-net-phy-add-genphy_c45_pma_read_ext_abilities.patch b/target/linux/generic/backport-6.6/750-01-v6.8-net-phy-add-genphy_c45_pma_read_ext_abilities.patch new file mode 100644 index 0000000000..343dc3133f --- /dev/null +++ b/target/linux/generic/backport-6.6/750-01-v6.8-net-phy-add-genphy_c45_pma_read_ext_abilities.patch @@ -0,0 +1,180 @@ +From 0c476157085fe2ad13b9bec70ea672e86647fa1a Mon Sep 17 00:00:00 2001 +From: Oleksij Rempel +Date: Tue, 12 Dec 2023 06:41:43 +0100 +Subject: [PATCH] net: phy: c45: add genphy_c45_pma_read_ext_abilities() + function + +Move part of the genphy_c45_pma_read_abilities() code to a separate +function. + +Some PHYs do not implement PMA/PMD status 2 register (Register 1.8) but +do implement PMA/PMD extended ability register (Register 1.11). To make +use of it, we need to be able to access this part of code separately. + +Signed-off-by: Oleksij Rempel +Reviewed-by: Andrew Lunn +Reviewed-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/20231212054144.87527-2-o.rempel@pengutronix.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy-c45.c | 129 ++++++++++++++++++++++---------------- + include/linux/phy.h | 1 + + 2 files changed, 75 insertions(+), 55 deletions(-) + +--- a/drivers/net/phy/phy-c45.c ++++ b/drivers/net/phy/phy-c45.c +@@ -920,6 +920,79 @@ int genphy_c45_pma_baset1_read_abilities + EXPORT_SYMBOL_GPL(genphy_c45_pma_baset1_read_abilities); + + /** ++ * genphy_c45_pma_read_ext_abilities - read supported link modes from PMA ++ * @phydev: target phy_device struct ++ * ++ * Read the supported link modes from the PMA/PMD extended ability register ++ * (Register 1.11). ++ */ ++int genphy_c45_pma_read_ext_abilities(struct phy_device *phydev) ++{ ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); ++ if (val < 0) ++ return val; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBLRM); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBT); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBKX4); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10GBKR); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_1000BT); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_1000BKX); ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_100BTX); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_100BTX); ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10BT); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ phydev->supported, ++ val & MDIO_PMA_EXTABLE_10BT); ++ ++ if (val & MDIO_PMA_EXTABLE_NBT) { ++ 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); ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, ++ phydev->supported, ++ val & MDIO_PMA_NG_EXTABLE_5GBT); ++ } ++ ++ if (val & MDIO_PMA_EXTABLE_BT1) { ++ val = genphy_c45_pma_baset1_read_abilities(phydev); ++ if (val < 0) ++ return val; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(genphy_c45_pma_read_ext_abilities); ++ ++/** + * genphy_c45_pma_read_abilities - read supported link modes from PMA + * @phydev: target phy_device struct + * +@@ -962,63 +1035,9 @@ int genphy_c45_pma_read_abilities(struct + val & MDIO_PMA_STAT2_10GBER); + + if (val & MDIO_PMA_STAT2_EXTABLE) { +- val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE); ++ val = genphy_c45_pma_read_ext_abilities(phydev); + if (val < 0) + return val; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBLRM); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBT); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBKX4); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10GBKR); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_1000BT); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_1000BKX); +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_100BTX); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_100BTX); +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10BT); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, +- phydev->supported, +- val & MDIO_PMA_EXTABLE_10BT); +- +- if (val & MDIO_PMA_EXTABLE_NBT) { +- 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); +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, +- phydev->supported, +- val & MDIO_PMA_NG_EXTABLE_5GBT); +- } +- +- if (val & MDIO_PMA_EXTABLE_BT1) { +- val = genphy_c45_pma_baset1_read_abilities(phydev); +- if (val < 0) +- return val; +- } + } + + /* This is optional functionality. If not supported, we may get an error +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1861,6 +1861,7 @@ int genphy_c45_an_config_aneg(struct phy + int genphy_c45_an_disable_aneg(struct phy_device *phydev); + int genphy_c45_read_mdix(struct phy_device *phydev); + int genphy_c45_pma_read_abilities(struct phy_device *phydev); ++int genphy_c45_pma_read_ext_abilities(struct phy_device *phydev); + int genphy_c45_pma_baset1_read_abilities(struct phy_device *phydev); + int genphy_c45_read_eee_abilities(struct phy_device *phydev); + int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev); diff --git a/target/linux/generic/backport-6.6/750-02-v6.12-net-phy-motorcomm-prepare-for-yt8821-support.patch b/target/linux/generic/backport-6.6/750-02-v6.12-net-phy-motorcomm-prepare-for-yt8821-support.patch new file mode 100644 index 0000000000..d566926ad3 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-02-v6.12-net-phy-motorcomm-prepare-for-yt8821-support.patch @@ -0,0 +1,47 @@ +From 8d878c87b5c45ae64b0aecd4aac71e210d19173f Mon Sep 17 00:00:00 2001 +From: Frank Sae +Date: Sun, 1 Sep 2024 01:35:25 -0700 +Subject: [PATCH] net: phy: Optimize phy speed mask to be compatible to yt8821 + +yt8521 and yt8531s as Gigabit transceiver use bit15:14(bit9 reserved +default 0) as phy speed mask, yt8821 as 2.5G transceiver uses bit9 bit15:14 +as phy speed mask. + +Be compatible to yt8821, reform phy speed mask and phy speed macro. + +Reviewed-by: Andrew Lunn +Signed-off-by: Frank Sae +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/motorcomm.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -46,12 +46,10 @@ + + /* Specific Status Register */ + #define YTPHY_SPECIFIC_STATUS_REG 0x11 +-#define YTPHY_SSR_SPEED_MODE_OFFSET 14 +- +-#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14)) +-#define YTPHY_SSR_SPEED_10M 0x0 +-#define YTPHY_SSR_SPEED_100M 0x1 +-#define YTPHY_SSR_SPEED_1000M 0x2 ++#define YTPHY_SSR_SPEED_MASK ((0x3 << 14) | BIT(9)) ++#define YTPHY_SSR_SPEED_10M ((0x0 << 14)) ++#define YTPHY_SSR_SPEED_100M ((0x1 << 14)) ++#define YTPHY_SSR_SPEED_1000M ((0x2 << 14)) + #define YTPHY_SSR_DUPLEX_OFFSET 13 + #define YTPHY_SSR_DUPLEX BIT(13) + #define YTPHY_SSR_PAGE_RECEIVED BIT(12) +@@ -1187,8 +1185,7 @@ static int yt8521_adjust_status(struct p + else + duplex = DUPLEX_FULL; /* for fiber, it always DUPLEX_FULL */ + +- speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >> +- YTPHY_SSR_SPEED_MODE_OFFSET; ++ speed_mode = status & YTPHY_SSR_SPEED_MASK; + + switch (speed_mode) { + case YTPHY_SSR_SPEED_10M: diff --git a/target/linux/generic/backport-6.6/750-03-v6.12-net-phy-motorcomm-add-yt8821-support.patch b/target/linux/generic/backport-6.6/750-03-v6.12-net-phy-motorcomm-add-yt8821-support.patch new file mode 100644 index 0000000000..78bec326d0 --- /dev/null +++ b/target/linux/generic/backport-6.6/750-03-v6.12-net-phy-motorcomm-add-yt8821-support.patch @@ -0,0 +1,751 @@ +From b671105b88c3bb9acc1fb61a3ee2ca0ece60cb8d Mon Sep 17 00:00:00 2001 +From: Frank Sae +Date: Sun, 1 Sep 2024 01:35:26 -0700 +Subject: [PATCH] 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 +Reviewed-by: Sai Krishna +Reviewed-by: Andrew Lunn +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/motorcomm.c | 671 +++++++++++++++++++++++++++++++++++- + 1 file changed, 667 insertions(+), 4 deletions(-) + +--- 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 + * Author: Frank +@@ -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 ph + 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_d + .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 __may + { 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 */ } + }; +