kernel: net: phy: realtek: replace in-band AN hack
authorDaniel Golle <daniel@makrotopia.org>
Sat, 3 Jan 2026 15:41:17 +0000 (15:41 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Wed, 7 Jan 2026 01:30:26 +0000 (01:30 +0000)
Replace downstream hack for RealTek PHYs with a more clean solution
which could make it upstream.

As SGMII in-band AN is broken on some platforms, or simply expected to
be disabled by default in phy/sgmii mode (ie. on-board PHYs with MDIO
for out-of-band configuration and status), a hack for the RealTek PHY
driver was introduced to unconditionally disable SGMII in-band
autonegotiation.

Meanwhile the kernel has gained a proper interface for PHY and PCS to
report in-band AN capabilities and enable/disable in-band, matching
PHY and PCS capabilities.

Thanks to Bevan Weiss' knowledge about how RealTek PHY SerDes registers
are being handled in RealTek's SDK this can now be greatly improved:
 - report in-band capabilties
 - let phylink set in-band matching PCS and PHY capabilities
 - properly abstracted indirect access of SerDes registers

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
target/linux/generic/pending-6.12/720-02-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch [deleted file]
target/linux/generic/pending-6.12/720-02-net-phy-realtek-implement-configuring-in-band-an.patch [new file with mode: 0644]
target/linux/generic/pending-6.12/720-03-net-phy-realtek-make-sure-paged-read-is-protected-by.patch
target/linux/generic/pending-6.12/720-04-net-phy-realtek-setup-aldps.patch
target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
target/linux/generic/pending-6.12/720-07-net-phy-realtek-mark-existing-MMDs-as-present.patch
target/linux/generic/pending-6.12/720-09-net-phy-realtek-disable-MDIO-broadcast.patch

diff --git a/target/linux/generic/pending-6.12/720-02-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch b/target/linux/generic/pending-6.12/720-02-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch
deleted file mode 100644 (file)
index 4ee4e76..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-From d54ef6aea00e7a6ace439baade6ad0aa38ee4b04 Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 3 Apr 2023 01:21:57 +0300
-Subject: [PATCH 287/326] net: phy: realtek: disable SGMII in-band AN for 2.5G
- PHYs
-
-MAC drivers don't use SGMII in-band autonegotiation unless told to do so
-in device tree using 'managed = "in-band-status"'. When using MDIO to
-access a PHY, in-band-status is unneeded as we have link-status via
-MDIO. Switch off SGMII in-band autonegotiation using magic values.
-
-Reported-by: Chen Minqiang <ptpt52@gmail.com>
-Reported-by: Chukun Pan <amadeus@jmu.edu.cn>
-Reported-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
-Tested-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++++++++++--
- 1 file changed, 25 insertions(+), 2 deletions(-)
-
---- a/drivers/net/phy/realtek/realtek_main.c
-+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1345,8 +1345,8 @@ static int rtl822xb_write_mmd(struct phy
- static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1)
- {
-       bool has_2500, has_sgmii;
-+      int ret, val;
-       u16 mode;
--      int ret;
-       has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX,
-                           phydev->host_interfaces) ||
-@@ -1388,18 +1388,42 @@ static int rtl822x_set_serdes_option_mod
-                                    RTL822X_VND1_SERDES_OPTION,
-                                    RTL822X_VND1_SERDES_OPTION_MODE_MASK,
-                                    mode);
--      if (gen1 || ret < 0)
-+      if (ret < 0)
-+              return ret;
-+
-+      if (!gen1) {
-+              ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503);
-+              if (ret < 0)
-+                      return ret;
-+
-+              ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455);
-+              if (ret < 0)
-+                      return ret;
-+
-+              ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
-+              if (ret < 0)
-+                      return ret;
-+      }
-+
-+      /* Disable SGMII AN */
-+      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7588, 0x2);
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7589, 0x71d0);
-+      if (ret < 0)
-               return ret;
--      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503);
-+      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7587, 0x3);
-       if (ret < 0)
-               return ret;
--      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455);
-+      ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, 0x7587,
-+                                      val, !(val & BIT(0)), 500, 100000, false);
-       if (ret < 0)
-               return ret;
--      return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
-+      return 0;
- }
- static int rtl822x_config_init(struct phy_device *phydev)
diff --git a/target/linux/generic/pending-6.12/720-02-net-phy-realtek-implement-configuring-in-band-an.patch b/target/linux/generic/pending-6.12/720-02-net-phy-realtek-implement-configuring-in-band-an.patch
new file mode 100644 (file)
index 0000000..b0b56ac
--- /dev/null
@@ -0,0 +1,146 @@
+From 43a4dfb71e2d23bae10ae13b7314d0641321d35e Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 3 Jan 2026 02:53:59 +0000
+Subject: [PATCH 2/2] net: phy: realtek: implement configuring in-band an
+
+Implement the inband_caps() and config_inband() PHY driver methods to
+allow configuring the use of in-band-status with SGMII and 2500Base-X on
+RTL8226 and RTL8221B 2.5GE PHYs.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek/realtek_main.c | 67 ++++++++++++++++++++++++++
+ 1 file changed, 67 insertions(+)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -135,6 +135,15 @@
+ #define RTL822X_VND2_TO_PAGE_REG(reg)         (16 + (((reg) & GENMASK(3, 0)) >> 1))
+ #define RTL822X_VND2_C22_REG(reg)             (0xa400 + 2 * (reg))
++#define RTL822X_VND1_SERDES_CMD                       0x7587
++#define  RTL822X_VND1_SERDES_CMD_WRITE                BIT(1)
++#define  RTL822X_VND1_SERDES_CMD_BUSY         BIT(0)
++#define RTL822X_VND1_SERDES_ADDR              0x7588
++#define  RTL822X_VND1_SERDES_ADDR_AUTONEG     0x2
++#define   RTL822X_VND1_SERDES_INBAND_DISABLE  0x71d0
++#define   RTL822X_VND1_SERDES_INBAND_ENABLE   0x70d0
++#define RTL822X_VND1_SERDES_DATA              0x7589
++
+ #define RTL8221B_VND2_INER                    0xa4d2
+ #define RTL8221B_VND2_INER_LINK_STATUS                BIT(4)
+@@ -1381,6 +1390,50 @@ static int rtl822xb_config_init(struct p
+       return rtl822x_set_serdes_option_mode(phydev, false);
+ }
++static int rtl822x_serdes_write(struct phy_device *phydev, u16 reg, u16 val)
++{
++      int ret, poll;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_ADDR, reg);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_DATA, val);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CMD,
++                          RTL822X_VND1_SERDES_CMD_WRITE |
++                          RTL822X_VND1_SERDES_CMD_BUSY);
++      if (ret < 0)
++              return ret;
++
++      return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
++                                       RTL822X_VND1_SERDES_CMD, poll,
++                                       !(poll & RTL822X_VND1_SERDES_CMD_BUSY),
++                                       500, 100000, false);
++}
++
++static int rtl822x_config_inband(struct phy_device *phydev, unsigned int modes)
++{
++      return rtl822x_serdes_write(phydev, RTL822X_VND1_SERDES_ADDR_AUTONEG,
++                                  (modes != LINK_INBAND_DISABLE) ?
++                                  RTL822X_VND1_SERDES_INBAND_ENABLE :
++                                  RTL822X_VND1_SERDES_INBAND_DISABLE);
++}
++
++static unsigned int rtl822x_inband_caps(struct phy_device *phydev,
++                                       phy_interface_t interface)
++{
++      switch (interface) {
++      case PHY_INTERFACE_MODE_2500BASEX:
++      case PHY_INTERFACE_MODE_SGMII:
++              return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
++      default:
++              return 0;
++      }
++}
++
+ static int rtl822xb_get_rate_matching(struct phy_device *phydev,
+                                     phy_interface_t iface)
+ {
+@@ -2180,6 +2233,8 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .inband_caps    = rtl822x_inband_caps,
++              .config_inband  = rtl822x_config_inband,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+@@ -2195,6 +2250,8 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_c45_get_features,
+               .config_aneg    = rtl822x_c45_config_aneg,
+               .config_init    = rtl822x_config_init,
++              .inband_caps    = rtl822x_inband_caps,
++              .config_inband  = rtl822x_config_inband,
+               .read_status    = rtl822xb_c45_read_status,
+               .suspend        = genphy_c45_pma_suspend,
+               .resume         = rtlgen_c45_resume,
+@@ -2207,6 +2264,8 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .inband_caps    = rtl822x_inband_caps,
++              .config_inband  = rtl822x_config_inband,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+@@ -2223,6 +2282,8 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .inband_caps    = rtl822x_inband_caps,
++              .config_inband  = rtl822x_config_inband,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+@@ -2239,6 +2300,8 @@ static struct phy_driver realtek_drvs[]
+               .soft_reset     = rtl822x_c45_soft_reset,
+               .probe          = rtl822x_probe,
+               .config_init    = rtl822xb_config_init,
++              .inband_caps    = rtl822x_inband_caps,
++              .config_inband  = rtl822x_config_inband,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .get_features   = rtl822x_c45_get_features,
+               .config_aneg    = rtl822x_c45_config_aneg,
+@@ -2253,6 +2316,8 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .inband_caps    = rtl822x_inband_caps,
++              .config_inband  = rtl822x_config_inband,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+@@ -2269,6 +2334,8 @@ static struct phy_driver realtek_drvs[]
+               .soft_reset     = rtl822x_c45_soft_reset,
+               .probe          = rtl822x_probe,
+               .config_init    = rtl822xb_config_init,
++              .inband_caps    = rtl822x_inband_caps,
++              .config_inband  = rtl822x_config_inband,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .get_features   = rtl822x_c45_get_features,
+               .config_aneg    = rtl822x_c45_config_aneg,
index 710adf7e7a5649f3bcf34ef811b8bb41e8b57549..d799cd2b631f2ee9545dc2611f20da4eeca66dff 100644 (file)
@@ -18,7 +18,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1815,9 +1815,11 @@ static bool rtlgen_supports_2_5gbps(stru
+@@ -1813,9 +1813,11 @@ static bool rtlgen_supports_2_5gbps(stru
  {
        int val;
  
index d4a625a76ac9ff9c18e9a1ae5f9d04e3b1a8a508..3e6b23f58adb31eb9a27b8019cbd0c79d02a2d84 100644 (file)
@@ -13,7 +13,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -162,6 +162,10 @@
+@@ -170,6 +170,10 @@
  
  #define RTL8224_SRAM_RTCT_LEN(pair)           (0x8028 + (pair) * 4)
  
@@ -24,7 +24,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  #define RTL8366RB_POWER_SAVE                  0x15
  #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
  
-@@ -208,6 +212,10 @@ struct rtl821x_priv {
+@@ -216,6 +220,10 @@ struct rtl821x_priv {
        u16 iner;
  };
  
@@ -35,7 +35,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static int rtl821x_read_page(struct phy_device *phydev)
  {
        return __phy_read(phydev, RTL821x_PAGE_SELECT);
-@@ -1231,6 +1239,18 @@ static int rtl822x_write_mmd(struct phy_
+@@ -1239,6 +1247,18 @@ static int rtl822x_write_mmd(struct phy_
  
  static int rtl822x_probe(struct phy_device *phydev)
  {
@@ -54,10 +54,10 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
        if (IS_ENABLED(CONFIG_REALTEK_PHY_HWMON) &&
            phydev->phy_id != RTL_GENERIC_PHYID)
                return rtl822x_hwmon_init(phydev);
-@@ -1342,6 +1362,19 @@ static int rtl822xb_write_mmd(struct phy
-       return write_ret;
+@@ -1320,6 +1340,19 @@ static int rtl822xb_write_mmd(struct phy
  }
  
 +static int rtl822x_init_phycr1(struct phy_device *phydev, bool no_aldps)
 +{
 +      struct rtl822x_priv *priv = phydev->priv;
@@ -74,10 +74,11 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1)
  {
        bool has_2500, has_sgmii;
-@@ -1423,6 +1456,14 @@ static int rtl822x_set_serdes_option_mod
+@@ -1377,7 +1410,15 @@ static int rtl822x_set_serdes_option_mod
        if (ret < 0)
                return ret;
  
+-      return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
 +      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
 +      if (ret < 0)
 +              return ret;
@@ -86,6 +87,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 +      if (ret < 0)
 +              return ret;
 +
-       return 0;
++      return 0;
  }
  
+ static int rtl822x_config_init(struct phy_device *phydev)
index 7afa1595887bdfbdf578c850506e4343d5ce5f70..c46942697f4ae9920d05ae72b75640afc8b4b1fd 100644 (file)
@@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1900,10 +1900,32 @@ static int rtl8226_match_phy_device(stru
+@@ -1898,10 +1898,32 @@ static int rtl8226_match_phy_device(stru
  static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
                               bool is_c45)
  {
index 50fa7c9efc85fc0cca6f2ae7d964628e1232c6f1..12663d334b7d52d64b1e259a7c6542da4b6469a9 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1597,6 +1597,9 @@ static int rtl822x_c45_get_features(stru
+@@ -1626,6 +1626,9 @@ static int rtl822x_c45_get_features(stru
        linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT,
                         phydev->supported);
  
index a23c88ebe5b06bddc952a33afc32903a4aeff070..e990bcc6feb122c66d84bb5ad2251481216a5d92 100644 (file)
@@ -13,7 +13,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 ---
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -165,6 +165,7 @@
+@@ -174,6 +174,7 @@
  #define RTL8221B_PHYCR1                               0xa430
  #define RTL8221B_PHYCR1_ALDPS_EN              BIT(2)
  #define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN     BIT(12)
@@ -21,7 +21,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
  
  #define RTL8366RB_POWER_SAVE                  0x15
  #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
-@@ -1372,7 +1373,8 @@ static int rtl822x_init_phycr1(struct ph
+@@ -1381,7 +1382,8 @@ static int rtl822x_init_phycr1(struct ph
  
        return phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL8221B_PHYCR1,
                                      RTL8221B_PHYCR1_ALDPS_EN |