Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ericwoud/d912301a93cd41b39621a65cc372a5c0 to your computer and use it in GitHub Desktop.

Select an option

Save ericwoud/d912301a93cd41b39621a65cc372a5c0 to your computer and use it in GitHub Desktop.
SFP OEM SFP-2.5G-T patches
/*
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 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
@frank-w
Copy link

frank-w commented Aug 22, 2023

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

@ericwoud
Copy link
Author

ericwoud commented Aug 22, 2023

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.

@dangowrt
Copy link

@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.

@ericwoud
Copy link
Author

ericwoud commented Aug 22, 2023

@dangowrt

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment