-
-
Save ericwoud/d912301a93cd41b39621a65cc372a5c0 to your computer and use it in GitHub Desktop.
| /* | |
| Old news in this gist, development continues here: | |
| https://github.com/ericwoud/linux/tree/net-phy-realtek-next | |
| to create pull request to: | |
| https://github.com/dangowrt/linux/tree/net-phy-realtek-next | |
| ////////////////////////////////////////////////////////////// | |
| This file is only added for reading and keeping the link to this page in tact... | |
| Only use the 000*.patch files. | |
| Based on (partly included in 0006.patch): | |
| [PATCH] net: phy: realtek: rtl8221: allow to configure SERDES mode | |
| [PATCH] net: phy: realtek: disable SGMII in-band AN for 2.5G PHYs | |
| By Daniel Golle to add interface switching | |
| */ | |
| From 63ef1c65a0431dd6f6aa6f64c7109d93c501150d Mon Sep 17 00:00:00 2001 | |
| From: Eric Woudstra <ericwouds@gmail.com> | |
| Date: Fri, 28 Jul 2023 16:01:56 +0200 | |
| Subject: [PATCH 1/5] net: phy: sfp: Fixup for OEM SFP-2.5G-T module | |
| Implement fixup for OEM SFP-2.5G-T module. It can now recognise and attach | |
| the rtl8221B, using the RollBall protocol. | |
| Remove the quirk. With implementing the fixup, the PHY is attached, so the | |
| values changed in the quirk are no longer used. | |
| --- | |
| drivers/net/phy/sfp.c | 27 +++++++++------------------ | |
| 1 file changed, 9 insertions(+), 18 deletions(-) | |
| diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c | |
| index d855a1830..91262d1ea 100644 | |
| --- a/drivers/net/phy/sfp.c | |
| +++ b/drivers/net/phy/sfp.c | |
| @@ -363,6 +363,14 @@ static void sfp_fixup_rollball_proto(struct sfp *sfp, unsigned int secs) | |
| sfp->module_t_wait = msecs_to_jiffies(secs * 1000); | |
| } | |
| +// For 2.5GBASE-T short-reach modules | |
| +static void sfp_fixup_oem_2_5g(struct sfp *sfp) | |
| +{ | |
| + sfp_fixup_rollball_proto(sfp, 4); | |
| + sfp->id.base.connector = SFF8024_CONNECTOR_RJ45; | |
| + sfp->id.base.extended_cc = SFF8024_ECC_2_5GBASE_T; | |
| +} | |
| + | |
| static void sfp_fixup_fs_10gt(struct sfp *sfp) | |
| { | |
| sfp_fixup_10gbaset_30m(sfp); | |
| @@ -404,23 +412,6 @@ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, | |
| __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); | |
| } | |
| -static void sfp_quirk_disable_autoneg(const struct sfp_eeprom_id *id, | |
| - unsigned long *modes, | |
| - unsigned long *interfaces) | |
| -{ | |
| - linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, modes); | |
| -} | |
| - | |
| -static void sfp_quirk_oem_2_5g(const struct sfp_eeprom_id *id, | |
| - unsigned long *modes, | |
| - unsigned long *interfaces) | |
| -{ | |
| - /* Copper 2.5G SFP */ | |
| - linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, modes); | |
| - __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); | |
| - sfp_quirk_disable_autoneg(id, modes, interfaces); | |
| -} | |
| - | |
| static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, | |
| unsigned long *modes, | |
| unsigned long *interfaces) | |
| @@ -475,7 +466,7 @@ static const struct sfp_quirk sfp_quirks[] = { | |
| SFP_QUIRK_F("Walsun", "HXSX-ATRI-1", sfp_fixup_fs_10gt), | |
| SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc), | |
| - SFP_QUIRK_M("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g), | |
| + SFP_QUIRK_F("OEM", "SFP-2.5G-T", sfp_fixup_oem_2_5g), | |
| SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc), | |
| SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc), | |
| SFP_QUIRK_F("Turris", "RTSFP-10", sfp_fixup_rollball), | |
| -- | |
| 2.40.1 |
| From 8c95e8416ce21b6ada4d02e1be5d022eff5242ef Mon Sep 17 00:00:00 2001 | |
| From: Eric Woudstra <ericwouds@gmail.com> | |
| Date: Fri, 28 Jul 2023 16:06:51 +0200 | |
| Subject: [PATCH 2/5] net: phylink: rtl822x SFP module no inband negotiation | |
| The rtl822x does not seem to support inband negotiation, when it is in | |
| "Rate Adaptor Mode". The datasheet has very limited information about | |
| this mode. | |
| Because there is no openly documented way to determine if the rtl822x is in | |
| "Rate Adaptor Mode", assume when a rtl822x is usiing RollBall protocol | |
| address, it is setup in this mode. Can change this, if a better option is | |
| known to detect this mode. | |
| --- | |
| drivers/net/phy/phylink.c | 14 +++++++++++--- | |
| 1 file changed, 11 insertions(+), 3 deletions(-) | |
| diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c | |
| index 4f1c8bb19..d25063245 100644 | |
| --- a/drivers/net/phy/phylink.c | |
| +++ b/drivers/net/phy/phylink.c | |
| @@ -3253,11 +3253,19 @@ static void phylink_sfp_link_up(void *upstream) | |
| phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK); | |
| } | |
| -/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII | |
| - * or 802.3z control word, so inband will not work. | |
| - */ | |
| static bool phylink_phy_no_inband(struct phy_device *phy) | |
| { | |
| +/* Assume when a rtl8221_vb_cg is accessed via RollBall address, it is setup | |
| +* in "Rate Adaptor Mode", until a better option is known to detect this mode. | |
| +*/ | |
| + if (phy->is_c45 && | |
| + phy->c45_ids.device_ids[1] == 0x001cc849 && | |
| + phy->mdio.addr == 17) | |
| + return true; | |
| + | |
| + /* The Broadcom BCM84881 in the Methode DM7052 is unable to provide | |
| + * a SGMII or 802.3z control word, so inband will not work. | |
| + */ | |
| return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1], | |
| 0xae025150, 0xfffffff0); | |
| } | |
| -- | |
| 2.40.1 |
| From bb4e4568af01630ce817a018c31b4b61dda7f013 Mon Sep 17 00:00:00 2001 | |
| From: Eric Woudstra <ericwouds@gmail.com> | |
| Date: Wed, 2 Aug 2023 17:06:39 +0200 | |
| Subject: [PATCH 3/5] net: phy: realtek: rtlgen_get_speed(): Pass register | |
| value as argument | |
| The register that needs to be read to determine the speed, is not | |
| always read in the same manner, also not always at the same address. | |
| To be able to use the one rtlgen_get_speed() function, generalize | |
| rtlgen_get_speed() even more. Pass the value from the device specific | |
| register as argument to rtlgen_get_speed(). | |
| --- | |
| drivers/net/phy/realtek.c | 24 +++++++++++++----------- | |
| 1 file changed, 13 insertions(+), 11 deletions(-) | |
| diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c | |
| index 894172a3e..e138a93ac 100644 | |
| --- a/drivers/net/phy/realtek.c | |
| +++ b/drivers/net/phy/realtek.c | |
| @@ -537,17 +537,11 @@ static int rtl8366rb_config_init(struct phy_device *phydev) | |
| } | |
| /* get actual speed to cover the downshift case */ | |
| -static int rtlgen_get_speed(struct phy_device *phydev) | |
| +static int rtlgen_get_speed(struct phy_device *phydev, int val) | |
| { | |
| - int val; | |
| - | |
| if (!phydev->link) | |
| return 0; | |
| - val = phy_read_paged(phydev, 0xa43, 0x12); | |
| - if (val < 0) | |
| - return val; | |
| - | |
| switch (val & RTLGEN_SPEED_MASK) { | |
| case 0x0000: | |
| phydev->speed = SPEED_10; | |
| @@ -576,13 +570,17 @@ static int rtlgen_get_speed(struct phy_device *phydev) | |
| static int rtlgen_read_status(struct phy_device *phydev) | |
| { | |
| - int ret; | |
| + int ret, val; | |
| ret = genphy_read_status(phydev); | |
| if (ret < 0) | |
| return ret; | |
| - return rtlgen_get_speed(phydev); | |
| + val = phy_read_paged(phydev, 0xa43, 0x12); | |
| + if (val < 0) | |
| + return val; | |
| + | |
| + return rtlgen_get_speed(phydev, val); | |
| } | |
| static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum) | |
| @@ -705,7 +703,7 @@ static int rtl822x_config_aneg(struct phy_device *phydev) | |
| static int rtl822x_read_status(struct phy_device *phydev) | |
| { | |
| - int ret; | |
| + int ret, val; | |
| if (phydev->autoneg == AUTONEG_ENABLE) { | |
| int lpadv = phy_read_paged(phydev, 0xa5d, 0x13); | |
| @@ -725,7 +723,11 @@ static int rtl822x_read_status(struct phy_device *phydev) | |
| if (ret < 0) | |
| return ret; | |
| - return rtlgen_get_speed(phydev); | |
| + val = phy_read_paged(phydev, 0xa43, 0x12); | |
| + if (val < 0) | |
| + return val; | |
| + | |
| + return rtlgen_get_speed(phydev, val); | |
| } | |
| static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) | |
| -- | |
| 2.40.1 |
| From 1afa0b08595bd6e227e9370189b31f6658988c1e Mon Sep 17 00:00:00 2001 | |
| From: Eric Woudstra <ericwouds@gmail.com> | |
| Date: Wed, 2 Aug 2023 17:08:03 +0200 | |
| Subject: [PATCH 4/5] net: phy: realtek: rtlgen_get_speed(): Add 2500-Lite mode | |
| Add 2500-Litemode, reporting 1000Mbps. | |
| --- | |
| drivers/net/phy/realtek.c | 3 +++ | |
| 1 file changed, 3 insertions(+) | |
| diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c | |
| index e138a93ac..7737e8173 100644 | |
| --- a/drivers/net/phy/realtek.c | |
| +++ b/drivers/net/phy/realtek.c | |
| @@ -561,6 +561,9 @@ static int rtlgen_get_speed(struct phy_device *phydev, int val) | |
| case 0x0220: | |
| phydev->speed = SPEED_5000; | |
| break; | |
| + case 0x0230: | |
| + phydev->speed = SPEED_1000; | |
| + break; | |
| default: | |
| break; | |
| } | |
| -- | |
| 2.40.1 |
| From 8be25416a6c6e2cffad9fe9f575df9e5d7362a76 Mon Sep 17 00:00:00 2001 | |
| From: Eric Woudstra <ericwouds@gmail.com> | |
| Date: Sun, 6 Aug 2023 19:27:35 +0200 | |
| Subject: [PATCH 5/6] net: phy: phy_device: Do not probe or read c45_id from | |
| Realtek PHY MDIO_MMD_VEND1 | |
| Realtek PHY do not support reading from MDIO_MMD_VEND1 at MDIO_STAT2 | |
| or MII_PHYSIDx. The registers are undocumented in the datasheet and | |
| reading from them makes the PHY behave erratically and fail. | |
| --- | |
| drivers/net/phy/phy_device.c | 8 ++++++++ | |
| 1 file changed, 8 insertions(+) | |
| diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c | |
| index 3e9909b30..2ae603d16 100644 | |
| --- a/drivers/net/phy/phy_device.c | |
| +++ b/drivers/net/phy/phy_device.c | |
| @@ -831,6 +831,14 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, | |
| if (!(devs_in_pkg & (1 << i))) | |
| continue; | |
| + /* Realtek PHY's do not support reading from MDIO_MMD_VEND1 | |
| + * at MDIO_STAT2 or MII_PHYSIDx. It makes them fail. | |
| + */ | |
| + if (i == MDIO_MMD_VEND1 && phy_id_compare( | |
| + c45_ids->device_ids[MDIO_MMD_PMAPMD], | |
| + 0x001cc800, GENMASK(31, 10))) | |
| + continue; | |
| + | |
| if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) { | |
| /* Probe the "Device Present" bits for the vendor MMDs | |
| * to ignore these if they do not contain IEEE 802.3 | |
| -- | |
| 2.40.1 |
| From a810647e4a2cc627ffb5bffeff9fad6270589cba Mon Sep 17 00:00:00 2001 | |
| From: Your Name <you@example.com> | |
| Date: Tue, 22 Aug 2023 16:14:41 +0200 | |
| Subject: [PATCH 6/6] net: phy: realtek: Add driver instance for RTL8221-VB-CG | |
| via C45 | |
| Add driver instance for clause-45 communication with RTL8221-VB-CG. The driver | |
| sets up a separate set of function pointers for clause-45 only accessable PHY's. | |
| The C45 driver instance checks if the RTL8221-VB-CG is in, what Realtek | |
| calls, "Rate Adaptor Mode". | |
| This new driver instance will be used by the OEM SFP-2.5G-T module. The | |
| RTL8221-VB-CG registers are setup by the onboard microprocessor that is also | |
| responsible for the RollBall protocol. | |
| --- | |
| drivers/net/phy/realtek.c | 317 +++++++++++++++++++++++++++++++++++++- | |
| 1 file changed, 315 insertions(+), 2 deletions(-) | |
| diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c | |
| index 7737e8173..651eaebfc 100644 | |
| --- a/drivers/net/phy/realtek.c | |
| +++ b/drivers/net/phy/realtek.c | |
| @@ -70,8 +70,46 @@ | |
| #define RTLGEN_SPEED_MASK 0x0630 | |
| +/* MMC VENDOR 1 */ | |
| + | |
| +#define RTL8221B_MMD_SERDES_CTRL MDIO_MMD_VEND1 | |
| +#define RTL8221B_SERDES_OPTION 0x697a | |
| +#define RTL8221B_SERDES_OPTION_MODE_MASK GENMASK(5, 0) | |
| +#define RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII 0 | |
| +#define RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII 1 | |
| +#define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2 | |
| +#define RTL8221B_SERDES_OPTION_MODE_HISGMII 3 | |
| + | |
| +#define RTL8221B_SERDES_CTRL1 0x6a04 | |
| +#define RTL8221B_SERDES_CTRL1_MDIOSEL_MASK 0x0700 | |
| +#define RTL8221B_SERDES_CTRL1_MDIOSEL_SGMII 0x0400 | |
| +#define RTL8221B_SERDES_CTRL1_MDIOSEL_HISGMII_2500BASEX 0x0500 | |
| + | |
| +#define RTL8221B_SERDES_CTRL3 0x7580 | |
| +#define RTL8221B_SERDES_CTRL3_MODE_MASK GENMASK(4, 0) | |
| +#define RTL8221B_SERDES_CTRL3_MODE_SGMII 0x02 | |
| +#define RTL8221B_SERDES_CTRL3_MODE_HISGMII 0x12 | |
| +#define RTL8221B_SERDES_CTRL3_MODE_2500BASEX 0x16 | |
| +#define RTL8221B_SERDES_CTRL3_MODE_OFF 0x1F | |
| +#define RTL8221B_SERDES_CTRL3_FORCE_LINK_DOWN BIT(5) | |
| + | |
| +/* MMC VENDOR 2 */ | |
| + | |
| +#define RTL8221B_MMD_PHY_CTRL MDIO_MMD_VEND2 | |
| + | |
| +#define RTL822x_GBCR 0xa412 | |
| +#define RTL822x_GBCR_1000BASET_FULL_DUPLEX_CAP BIT(9) | |
| + | |
| +#define RTL822x_GBSR 0xa414 | |
| +#define RTL822x_GBSR_LP_1000BASET_HALF_DUPLEX_CAP BIT(10) | |
| +#define RTL822x_GBSR_LP_1000BASET_FULL_DUPLEX_CAP BIT(11) | |
| + | |
| +#define RTL822x_PHYSR 0xa434 | |
| + | |
| #define RTL_GENERIC_PHYID 0x001cc800 | |
| #define RTL_8211FVD_PHYID 0x001cc878 | |
| +#define RTL_8221B_VB_CG 0x001cc849 | |
| + | |
| MODULE_DESCRIPTION("Realtek PHY driver"); | |
| MODULE_AUTHOR("Johnson Leung"); | |
| @@ -733,6 +771,143 @@ static int rtl822x_read_status(struct phy_device *phydev) | |
| return rtlgen_get_speed(phydev, val); | |
| } | |
| +static void rtl822x_update_interface(struct phy_device *phydev) | |
| +{ | |
| + int val; | |
| + | |
| + /* Automatically switch SERDES interface between | |
| + * SGMII and 2500-BaseX according to vendor register. | |
| + */ | |
| + | |
| + val = phy_read_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_SERDES_CTRL3); | |
| + if (val < 0) | |
| + return; | |
| + | |
| + //phydev_dbg(phydev, "rtl822x_update_interface: mode=0x%02lx\n", val & RTL8221B_SERDES_CTRL3_MODE_MASK); | |
| + | |
| + switch (val & RTL8221B_SERDES_CTRL3_MODE_MASK) { | |
| + case RTL8221B_SERDES_CTRL3_MODE_2500BASEX: | |
| + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; | |
| + break; | |
| + case RTL8221B_SERDES_CTRL3_MODE_SGMII: | |
| + phydev->interface = PHY_INTERFACE_MODE_SGMII; | |
| + break; | |
| + default: | |
| + break; | |
| + } | |
| +} | |
| + | |
| +static int rtl822x_c45_get_features(struct phy_device *phydev) | |
| +{ | |
| + int ret = 0; | |
| + | |
| + ret = genphy_c45_pma_read_abilities(phydev); | |
| + if (ret < 0) | |
| + return ret; | |
| + | |
| + linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, | |
| + phydev->supported); | |
| + | |
| + /* sfp_select_interface() only supports 2500baseX */ | |
| + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, | |
| + phydev->supported)) | |
| + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, | |
| + phydev->supported); | |
| + | |
| + phydev_dbg(phydev, | |
| + "rtl822x_c45_get_features: supported=%*pb\n", | |
| + __ETHTOOL_LINK_MODE_MASK_NBITS, phydev->supported); | |
| + | |
| + return ret; | |
| + | |
| +} | |
| + | |
| +static int rtl822x_c45_config_aneg(struct phy_device *phydev) | |
| +{ | |
| + bool changed = false; | |
| + int ret, val; | |
| + | |
| + /* Do not advertise 2500baseX */ | |
| + linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, | |
| + phydev->advertising); | |
| + | |
| + phydev_dbg(phydev, | |
| + "rtl822x_c45_config_aneg: %*pb\n", | |
| + __ETHTOOL_LINK_MODE_MASK_NBITS, phydev->advertising); | |
| + | |
| + if (phydev->autoneg == AUTONEG_DISABLE) | |
| + return genphy_c45_pma_setup_forced(phydev); | |
| + | |
| + ret = genphy_c45_an_config_aneg(phydev); | |
| + if (ret < 0) | |
| + return ret; | |
| + if (ret > 0) | |
| + changed = true; | |
| + | |
| + /* Clause 45 has no standardized support for 1000BaseT, therefore | |
| + * use vendor registers for this mode. | |
| + */ | |
| + val = 0; | |
| + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, | |
| + phydev->advertising)) | |
| + val |= RTL822x_GBCR_1000BASET_FULL_DUPLEX_CAP; | |
| + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822x_GBCR, | |
| + RTL822x_GBCR_1000BASET_FULL_DUPLEX_CAP, val); | |
| + if (ret < 0) | |
| + return ret; | |
| + if (ret > 0) | |
| + changed = true; | |
| + | |
| + return genphy_c45_check_and_restart_aneg(phydev, changed); | |
| +} | |
| + | |
| +static int rtl822x_c45_read_status(struct phy_device *phydev) | |
| +{ | |
| + int ret, val; | |
| + | |
| + ret = genphy_c45_read_status(phydev); | |
| + if (ret < 0) | |
| + return ret; | |
| + | |
| + /* Clause 45 has no standardized support for 1000BaseT, therefore | |
| + * use vendor registers for this mode. | |
| + */ | |
| + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822x_GBSR); | |
| + if (val < 0) | |
| + return val; | |
| + | |
| + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, | |
| + phydev->lp_advertising, | |
| + val & RTL822x_GBSR_LP_1000BASET_HALF_DUPLEX_CAP); | |
| + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, | |
| + phydev->lp_advertising, | |
| + val & RTL822x_GBSR_LP_1000BASET_FULL_DUPLEX_CAP); | |
| + | |
| + /* Read actual speed from vendor register. */ | |
| + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822x_PHYSR); | |
| + if (val < 0) | |
| + return val; | |
| + | |
| + ret = rtlgen_get_speed(phydev, val); | |
| + if (ret) | |
| + return ret; | |
| + | |
| + if (phydev->is_c45 && phydev->link) | |
| + rtl822x_update_interface(phydev); | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +static int rtl822x_c45_suspend(struct phy_device *phydev) | |
| +{ | |
| + return genphy_c45_pma_suspend(phydev); | |
| +} | |
| + | |
| +static int rtl822x_c45_resume(struct phy_device *phydev) | |
| +{ | |
| + return genphy_c45_pma_resume(phydev); | |
| +} | |
| + | |
| static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) | |
| { | |
| int val; | |
| @@ -756,6 +931,36 @@ static int rtl8226_match_phy_device(struct phy_device *phydev) | |
| rtlgen_supports_2_5gbps(phydev); | |
| } | |
| +/* Only use Clause 45 if available and Clause 22 is not. | |
| +* This also includes RollBall protocol SFP modules. | |
| +* Could possibly replace this function call with phydev->is_c45 | |
| +* to use the Clause 45 driver instance on all capable rtl822x. | |
| +*/ | |
| +static bool rtl822x_use_c45(struct phy_device *phydev) | |
| +{ | |
| + return !phydev->mdio.bus->read && !!phydev->mdio.bus->read_c45; | |
| +} | |
| + | |
| +static bool rtl822x_match_id(struct phy_device *phydev, u32 id) | |
| +{ | |
| + if (phydev->is_c45) | |
| + return id == phydev->c45_ids.device_ids[1]; | |
| + else | |
| + return id == phydev->phy_id; | |
| +} | |
| + | |
| +static int rtl8221_vb_cg_c45_match_phy_device(struct phy_device *phydev) | |
| +{ | |
| + return rtl822x_match_id(phydev, RTL_8221B_VB_CG) && | |
| + rtl822x_use_c45(phydev); | |
| +} | |
| + | |
| +static int rtl8221_vb_cg_c22_match_phy_device(struct phy_device *phydev) | |
| +{ | |
| + return rtl822x_match_id(phydev, RTL_8221B_VB_CG) && | |
| + !rtl822x_use_c45(phydev); | |
| +} | |
| + | |
| static int rtlgen_resume(struct phy_device *phydev) | |
| { | |
| int ret = genphy_resume(phydev); | |
| @@ -882,6 +1087,104 @@ static irqreturn_t rtl9000a_handle_interrupt(struct phy_device *phydev) | |
| return IRQ_HANDLED; | |
| } | |
| +static int rtl8221b_config_init(struct phy_device *phydev) | |
| +{ | |
| + u16 option_mode; | |
| + int val; | |
| + | |
| + if (!phy_interface_empty(phydev->host_interfaces)) { | |
| + if (!test_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->host_interfaces)) | |
| + return 0; | |
| + | |
| + /* check if rate adapter mode is needed */ | |
| + if (!test_bit(PHY_INTERFACE_MODE_SGMII, phydev->host_interfaces)) | |
| + option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX; | |
| + else | |
| + option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII; | |
| + } else { | |
| + switch (phydev->interface) { | |
| + case PHY_INTERFACE_MODE_2500BASEX: | |
| + /* use rate adapter mode if PHY is connected via C22 */ | |
| + if (!phydev->is_c45) { | |
| + option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX; | |
| + break; | |
| + } | |
| + fallthrough; | |
| + case PHY_INTERFACE_MODE_SGMII: | |
| + option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII; | |
| + break; | |
| + default: | |
| + return 0; | |
| + } | |
| + } | |
| + | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, | |
| + 0x75f3, 0); | |
| + | |
| + phy_modify_mmd_changed(phydev, RTL8221B_MMD_SERDES_CTRL, | |
| + RTL8221B_SERDES_OPTION, | |
| + RTL8221B_SERDES_OPTION_MODE_MASK, option_mode); | |
| + | |
| + switch (option_mode) { | |
| + case RTL8221B_SERDES_OPTION_MODE_2500BASEX: | |
| + phydev->rate_matching = RATE_MATCH_PAUSE; | |
| + fallthrough; | |
| + case RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII: | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503); | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd455); | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020); | |
| + break; | |
| + case RTL8221B_SERDES_OPTION_MODE_HISGMII: | |
| + phydev->rate_matching = RATE_MATCH_PAUSE; | |
| + fallthrough; | |
| + case RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII: | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503); | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd433); | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020); | |
| + break; | |
| + } | |
| + | |
| + /* Disable SGMII AN */ | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7588, 0x2); | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7589, 0x71d0); | |
| + phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587, 0x3); | |
| + phy_read_mmd_poll_timeout(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587, | |
| + val, !(val & BIT(0)), 500, 100000, false); | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +static int rtl822x_c45_config_init(struct phy_device *phydev) | |
| +{ | |
| + genphy_c45_pma_suspend(phydev); | |
| + | |
| + rtl8221b_config_init(phydev); | |
| + | |
| + genphy_c45_pma_resume(phydev); | |
| + | |
| + phydev_dbg(phydev, "rtl822x_c45_config_init\n"); | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +static int rtl822x_get_rate_matching(struct phy_device *phydev, | |
| + phy_interface_t iface) | |
| +{ | |
| + u32 option_mode; | |
| + int reg; | |
| + | |
| + reg = phy_read_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_SERDES_OPTION); | |
| + if (reg < 0) | |
| + return -EIO; | |
| + | |
| + option_mode = FIELD_GET(RTL8221B_SERDES_OPTION_MODE_MASK, reg); | |
| + if (option_mode == RTL8221B_SERDES_OPTION_MODE_2500BASEX || | |
| + option_mode == RTL8221B_SERDES_OPTION_MODE_HISGMII) | |
| + return RATE_MATCH_PAUSE; | |
| + | |
| + return RATE_MATCH_NONE; | |
| +} | |
| + | |
| static struct phy_driver realtek_drvs[] = { | |
| { | |
| PHY_ID_MATCH_EXACT(0x00008201), | |
| @@ -1033,8 +1336,8 @@ static struct phy_driver realtek_drvs[] = { | |
| .read_page = rtl821x_read_page, | |
| .write_page = rtl821x_write_page, | |
| }, { | |
| - PHY_ID_MATCH_EXACT(0x001cc849), | |
| - .name = "RTL8221B-VB-CG 2.5Gbps PHY", | |
| + .match_phy_device = rtl8221_vb_cg_c22_match_phy_device, | |
| + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", | |
| .get_features = rtl822x_get_features, | |
| .config_aneg = rtl822x_config_aneg, | |
| .read_status = rtl822x_read_status, | |
| @@ -1042,6 +1345,16 @@ static struct phy_driver realtek_drvs[] = { | |
| .resume = rtlgen_resume, | |
| .read_page = rtl821x_read_page, | |
| .write_page = rtl821x_write_page, | |
| + }, { | |
| + .match_phy_device = rtl8221_vb_cg_c45_match_phy_device, | |
| + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", | |
| + .config_init = &rtl822x_c45_config_init, | |
| + .get_rate_matching = rtl822x_get_rate_matching, | |
| + .get_features = rtl822x_c45_get_features, | |
| + .config_aneg = rtl822x_c45_config_aneg, | |
| + .read_status = rtl822x_c45_read_status, | |
| + .suspend = rtl822x_c45_suspend, | |
| + .resume = rtl822x_c45_resume, | |
| }, { | |
| PHY_ID_MATCH_EXACT(0x001cc84a), | |
| .name = "RTL8221B-VM-CG 2.5Gbps PHY", | |
| -- | |
| 2.40.1 |
My initial experience was also that SGMII was not working with RTL8221B/RTL8226 PHY, and that's because they enabled SGMII in-band-status by default and Linux assumes SGMII in-band-status to not be enabled when connecting a MAC to a PHY. Hence a patch was needed to switch off in-band-status on the PHY:
Maybe thats the cause i get no traffic with this sfp in sfp-lan slot? Still wonder why it was not recognized in sfp-wan slot on r4
This is about fixing sgmii. The version you tried was still only 2500baseX.
So it should be working and you cannot reproduce it...so it is maybe a problem in combination with r4 and i have to test with r3 again
Really can't say if it should or shouldn't work on R4.
All I know it works on R3. The recent changes I tested on eth1.
Link goes up and pinging works.
@eicwoud It would be great if you can make a pull request with your changes on top of the tree I have prepared here:
https://github.com/dangowrt/linux/tree/net-phy-realtek-next
Then we can discuss and improve the patches together and submit them once they work fine and look good enough.
I quickly converted... No time to test yet. You can check it out:
https://github.com/ericwoud/linux/commits/net-phy-realtek-next
I have changed the code here a bit to store the id in phydev->c45_ids.device_ids[1] for the early 8221b.
The ID is used to match for no_inband (sfp),
Edit: A bit different then on the repo before, I would like to do something like this, but I do not know for sure if
th id itself on the early 8221b equals zero.
static int rtl8221b_vb_cg_match_phy_device(struct phy_device *phydev)
{
int val;
u32 id;
if (phydev->is_c45) {
if (phydev->c45_ids.device_ids[1] != 0)
return phydev->c45_ids.device_ids[1] == RTL_8221B_VB_CG;
} else {
if (phydev->phy_id != 0)
return phydev->phy_id == RTL_8221B_VB_CG;
}
if (phydev->mdio.bus->read_c45) {
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID1);
if (val < 0)
return 0;
id = val << 16;
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID2);
if (val < 0)
return 0;
id |= val;
} else {
val = phy_read(phydev, MII_PHYSID1);
if (val < 0)
return 0;
id = val << 16;
val = phy_read(phydev, MII_PHYSID2);
if (val < 0)
return 0;
id |= val;
}
if (id != RTL_8221B_VB_CG) return 0;
if (phydev->is_c45)
phydev->c45_ids.device_ids[1] = id;
else
phydev->phy_id = id;
return true;
}
To also reduce the times phy_read_mmd() / phy_read() is being used...
The 2 patches I need I think cannot apply as the are preceded by other patches...
If @dangowrt has a patch-set that can apply on netdev/net-next, then I can make my patch-set apply on his.