]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
kernel: realtek: replace RTL8226 MDI swap patch by upstream version 23493/head
authorJan Hoffmann <jan@3e8.eu>
Fri, 22 May 2026 19:39:52 +0000 (21:39 +0200)
committerJonas Jelonek <jelonek.jonas@gmail.com>
Sat, 23 May 2026 15:25:59 +0000 (17:25 +0200)
A version of this patch has been accepted upstream, so use it here.

Link: https://lore.kernel.org/netdev/177932162564.3801238.2549776951847746974.git-patchwork-notify@kernel.org/
Signed-off-by: Jan Hoffmann <jan@3e8.eu>
Link: https://github.com/openwrt/openwrt/pull/23493
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
target/linux/realtek/patches-6.18/030-v7.2-net-phy-realtek-support-MDI-swapping-for-RTL8226-CG.patch [new file with mode: 0644]
target/linux/realtek/patches-6.18/740-net-phy-realtek-support-MDI-swapping-for-RTL8226.patch [deleted file]
target/linux/realtek/patches-6.18/743-net-realtek-serdes-configuration.patch

diff --git a/target/linux/realtek/patches-6.18/030-v7.2-net-phy-realtek-support-MDI-swapping-for-RTL8226-CG.patch b/target/linux/realtek/patches-6.18/030-v7.2-net-phy-realtek-support-MDI-swapping-for-RTL8226-CG.patch
new file mode 100644 (file)
index 0000000..c711077
--- /dev/null
@@ -0,0 +1,215 @@
+From 0765570f330f526dd12a966a0a6a25a99da52fb4 Mon Sep 17 00:00:00 2001
+From: Jan Hoffmann <jan@3e8.eu>
+Date: Sat, 16 May 2026 21:03:45 +0200
+Subject: [PATCH] net: phy: realtek: support MDI swapping for RTL8226-CG
+
+Add support for configuring swapping of MDI pairs (ABCD->DCBA) when the
+property "enet-phy-pair-order" is specified.
+
+Unfortunately, no documentation about this feature is available, but
+this implementation still tries to avoid magic numbers and raw register
+numbers where it seems clear what is going on.
+
+As it is unknown whether the patching step can be safely reversed, only
+enabling MDI swapping is fully supported. A value of "0" for the "enet-
+phy-pair-order" property is not accepted if the PHY has already been
+patched for MDI swapping (however, this should not occur in practice).
+
+Some other Realtek PHYs also support similar mechanisms:
+
+- RTL8221B-VB-CG allows to configure MDI swapping via the same register,
+  but does not need the additional patching step. However, it is unclear
+  whether a driver implementation for that PHY is necessary, as it is
+  known to support configuration via strapping pins (which is working
+  fine at least in Zyxel XGS1210-12 rev B1).
+
+- The patching step seems to match the one for the integrated PHYs of
+  some Realtek PCIe/USB NICs (see for example the r8152 driver).
+
+For now, only implement this for the RTL8226-CG PHY, where it is needed
+for the switches Zyxel XGS1010-12 rev A1 and XGS1210-12 rev A1.
+
+Signed-off-by: Jan Hoffmann <jan@3e8.eu>
+Link: https://patch.msgid.link/20260516190456.387768-1-jan@3e8.eu
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/realtek/realtek_main.c | 154 +++++++++++++++++++++++++
+ 1 file changed, 154 insertions(+)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -181,6 +181,21 @@
+ #define RTL8224_VND1_MDI_PAIR_SWAP            0xa90
+ #define RTL8224_VND1_MDI_POLARITY_SWAP                0xa94
++#define RTL8226_VND1_UNKNOWN_6A21             0x6a21
++#define  RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN        BIT(5)
++
++#define RTL8226_VND2_UNKNOWN_D068             0xd068
++#define  RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG      BIT(1)
++#define  RTL8226_VND2_UNKNOWN_D068_PAIR_SEL   GENMASK(4, 3)
++#define RTL8226_VND2_ADCCAL_OFFSET            0xd06a
++
++#define RTL8226_VND2_RG_LPF_CAP_XG_P0_P1      0xbd5a
++#define RTL8226_VND2_RG_LPF_CAP_XG_P2_P3      0xbd5c
++#define RTL8226_VND2_RG_LPF_CAP_P0_P1         0xbc18
++#define RTL8226_VND2_RG_LPF_CAP_P2_P3         0xbc1a
++#define  RTL8226_RG_LPF_CAP_PAIR_A_MASK               GENMASK(4, 0)
++#define  RTL8226_RG_LPF_CAP_PAIR_B_MASK               GENMASK(12, 8)
++
+ #define RTL8366RB_POWER_SAVE                  0x15
+ #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
+@@ -1391,6 +1406,144 @@ static int rtl822x_init_phycr1(struct ph
+                                     mask, val);
+ }
++static int rtl8226_set_mdi_swap(struct phy_device *phydev, bool swap_enable)
++{
++      u16 val = swap_enable ? RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN : 0;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, RTL8226_VND1_UNKNOWN_6A21,
++                            RTL8226_VND1_UNKNOWN_6A21_MDI_SWAP_EN, val);
++}
++
++static int rtl8226_swap_rg_lpf_cap(struct phy_device *phydev, u32 reg_p0_p1, u32 reg_p2_p3)
++{
++      u16 val_p0, val_p1, val_p2, val_p3;
++      int ret;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1);
++      if (ret < 0)
++              return ret;
++
++      val_p0 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret);
++      val_p1 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret);
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3);
++      if (ret < 0)
++              return ret;
++
++      val_p2 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_A_MASK, ret);
++      val_p3 = FIELD_GET(RTL8226_RG_LPF_CAP_PAIR_B_MASK, ret);
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p0_p1,
++                           RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK,
++                           FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p3) |
++                           FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p2));
++      if (ret < 0)
++              return ret;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg_p2_p3,
++                           RTL8226_RG_LPF_CAP_PAIR_A_MASK | RTL8226_RG_LPF_CAP_PAIR_B_MASK,
++                           FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_A_MASK, val_p1) |
++                           FIELD_PREP(RTL8226_RG_LPF_CAP_PAIR_B_MASK, val_p0));
++}
++
++static int rtl8226_patch_mdi_swap(struct phy_device *phydev, bool swap_enable)
++{
++      u16 adccal_offset[4];
++      bool is_patched;
++      int ret;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068);
++      if (ret < 0)
++              return ret;
++
++      is_patched = !(ret & RTL8226_VND2_UNKNOWN_D068_MDI_SWAP_FLAG);
++
++      if (is_patched == swap_enable) {
++              /* Nothing to do */
++              return 0;
++      }
++
++      if (!swap_enable) {
++              /* Patching is only implemented one-way, see next comment. */
++              phydev_err(phydev, "MDI swapping disabled, but PHY is already patched.\n");
++              return -EINVAL;
++      }
++
++      /* The exact meaning of these bits is unknown. We only know that bit 1
++       * is used as a flag that swapping is already done.
++       */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068, 0x7, 0x1);
++      if (ret < 0)
++              return ret;
++
++      for (int i = 0; i < 4; i++) {
++              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068,
++                                   RTL8226_VND2_UNKNOWN_D068_PAIR_SEL,
++                                   FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i));
++              if (ret < 0)
++                      return ret;
++
++              ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET);
++              if (ret < 0)
++                      return ret;
++
++              adccal_offset[i] = ret;
++      }
++
++      for (int i = 0; i < 4; i++) {
++              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_UNKNOWN_D068,
++                                   RTL8226_VND2_UNKNOWN_D068_PAIR_SEL,
++                                   FIELD_PREP(RTL8226_VND2_UNKNOWN_D068_PAIR_SEL, i));
++              if (ret < 0)
++                      return ret;
++
++              ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8226_VND2_ADCCAL_OFFSET,
++                                  adccal_offset[3 - i]);
++              if (ret < 0)
++                      return ret;
++      }
++
++      ret = rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_XG_P0_P1,
++                                    RTL8226_VND2_RG_LPF_CAP_XG_P2_P3);
++      if (ret < 0)
++              return ret;
++
++      return rtl8226_swap_rg_lpf_cap(phydev, RTL8226_VND2_RG_LPF_CAP_P0_P1,
++                                     RTL8226_VND2_RG_LPF_CAP_P2_P3);
++}
++
++static int rtl8226_config_mdi_order(struct phy_device *phydev)
++{
++      u32 order;
++      bool swap_enable;
++      int ret;
++
++      ret = of_property_read_u32(phydev->mdio.dev.of_node, "enet-phy-pair-order", &order);
++
++      /* Property not present, nothing to do */
++      if (ret == -EINVAL || ret == -ENOSYS)
++              return 0;
++
++      if (ret)
++              return ret;
++
++      if (order & ~1)
++              return -EINVAL;
++
++      swap_enable = !!(order & 1);
++
++      ret = rtl8226_set_mdi_swap(phydev, swap_enable);
++      if (ret)
++              return ret;
++
++      return rtl8226_patch_mdi_swap(phydev, swap_enable);
++}
++
++static int rtl8226_probe(struct phy_device *phydev)
++{
++      return rtl8226_config_mdi_order(phydev);
++}
++
+ static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1)
+ {
+       bool has_2500, has_sgmii;
+@@ -3083,6 +3236,7 @@ static struct phy_driver realtek_drvs[]
+               .soft_reset     = rtl822x_c45_soft_reset,
+               .get_features   = rtl822x_c45_get_features,
+               .config_aneg    = rtl822x_c45_config_aneg,
++              .probe          = rtl8226_probe,
+               .config_init    = rtl822x_config_init,
+               .inband_caps    = rtl822x_inband_caps,
+               .config_inband  = rtl822x_config_inband,
diff --git a/target/linux/realtek/patches-6.18/740-net-phy-realtek-support-MDI-swapping-for-RTL8226.patch b/target/linux/realtek/patches-6.18/740-net-phy-realtek-support-MDI-swapping-for-RTL8226.patch
deleted file mode 100644 (file)
index 35d9587..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-From 672a9bfb2e01ecaf40e5b92e9cc564589ffc251d Mon Sep 17 00:00:00 2001
-From: Jan Hoffmann <jan@3e8.eu>
-Date: Tue, 23 Dec 2025 20:07:53 +0100
-Subject: [PATCH] net: phy: realtek: support MDI swapping for RTL8226
-
-Add support for configuring swapping of MDI pairs (ABCD->DCBA) when the
-property "enet-phy-pair-order" is specified.
-
-Unfortunately, no documentation about this feature is available, so the
-configuration involves magic values. Only enabling MDI swapping is
-supported, as it is unknown whether the patching step can be safely
-reversed.
-
-For now, only implement it for RTL8226, where it is needed to make the
-PHYs in Zyxel XGS1010-12 rev A1 work. However, parts of this code might
-also be useful for other PHYs in the future:
-
-RTL8221B also allows to configure MDI swapping via the same register,
-but does not need the additional patching step. Since it also supports
-configuration via strapping pins, there might not be any need for driver
-support on that PHY, though.
-
-The patching step itself seems to be the same which is also used by the
-integrated PHY of some Realtek PCIe/USB NICs.
-
-Signed-off-by: Jan Hoffmann <jan@3e8.eu>
----
- drivers/net/phy/realtek/realtek_main.c | 159 ++++++++++++++++++++++++-
- 1 file changed, 158 insertions(+), 1 deletion(-)
-
---- a/drivers/net/phy/realtek/realtek_main.c
-+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1514,6 +1514,148 @@ static unsigned int rtl822x_inband_caps(
-       }
- }
-+static int rtl8226_set_mdi_swap(struct phy_device *phydev, bool swap_enable)
-+{
-+      u16 val = swap_enable ? BIT(5) : 0;
-+
-+      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, 0x6a21, BIT(5), val);
-+}
-+
-+static int rtl8226_patch_mdi_swap(struct phy_device *phydev)
-+{
-+      int ret;
-+      u16 vals[4];
-+
-+      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xd068);
-+      if (ret < 0)
-+              return ret;
-+
-+      if (!(ret & BIT(1))) {
-+              /* already swapped */
-+              return 0;
-+      }
-+
-+      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xd068, 0x7, 0x1);
-+      if (ret < 0)
-+              return ret;
-+
-+      /* swap adccal_offset */
-+
-+      for (int i = 0; i < 4; i++) {
-+              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xd068, 0x3 << 3, i << 3);
-+              if (ret < 0)
-+                      return ret;
-+
-+              ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xd06a);
-+              if (ret < 0)
-+                      return ret;
-+
-+              vals[i] = ret;
-+      }
-+
-+      for (int i = 0; i < 4; i++) {
-+              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xd068, 0x3 << 3, i << 3);
-+              if (ret < 0)
-+                      return ret;
-+
-+              ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xd06a, vals[3 - i]);
-+              if (ret < 0)
-+                      return ret;
-+      }
-+
-+      /* swap rg_lpf_cap_xg */
-+
-+      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xbd5a);
-+      if (ret < 0)
-+              return ret;
-+
-+      vals[0] = ret & 0x1f;
-+      vals[1] = (ret >> 8) & 0x1f;
-+
-+      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xbd5c);
-+      if (ret < 0)
-+              return ret;
-+
-+      vals[2] = ret & 0x1f;
-+      vals[3] = (ret >> 8) & 0x1f;
-+
-+      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xbd5a, 0x1f1f,
-+              vals[3] | (vals[2] << 8));
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xbd5c, 0x1f1f,
-+              vals[1] | (vals[0] << 8));
-+      if (ret < 0)
-+              return ret;
-+
-+      /* swap rg_lpf_cap */
-+
-+      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xbc18);
-+      if (ret < 0)
-+              return ret;
-+
-+      vals[0] = ret & 0x1f;
-+      vals[1] = (ret >> 8) & 0x1f;
-+
-+      ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xbc1a);
-+      if (ret < 0)
-+              return ret;
-+
-+      vals[2] = ret & 0x1f;
-+      vals[3] = (ret >> 8) & 0x1f;
-+
-+      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xbc18, 0x1f1f,
-+              vals[3] | (vals[2] << 8));
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xbc1a, 0x1f1f,
-+              vals[1] | (vals[0] << 8));
-+      if (ret < 0)
-+              return ret;
-+
-+      return 0;
-+}
-+
-+static int rtl8226_config_mdi_order(struct phy_device *phydev)
-+{
-+      u32 order;
-+      int ret;
-+
-+      ret = of_property_read_u32(phydev->mdio.dev.of_node, "enet-phy-pair-order", &order);
-+
-+      /* Property not present, nothing to do */
-+      if (ret == -EINVAL)
-+              return 0;
-+
-+      if (ret)
-+              return ret;
-+
-+      /* Only enabling MDI swapping is supported */
-+      if (order != 1)
-+              return -EINVAL;
-+
-+      ret = rtl8226_set_mdi_swap(phydev, true);
-+      if (ret)
-+              return ret;
-+
-+      ret = rtl8226_patch_mdi_swap(phydev);
-+      return ret;
-+}
-+
-+static int rtl8226_config_init(struct phy_device *phydev)
-+{
-+      int ret;
-+
-+      ret = rtl8226_config_mdi_order(phydev);
-+      if (ret)
-+              return ret;
-+
-+      return rtl822x_config_init(phydev);
-+}
-+
-+
- static int rtl822xb_get_rate_matching(struct phy_device *phydev,
-                                     phy_interface_t iface)
- {
-@@ -3083,7 +3225,7 @@ static struct phy_driver realtek_drvs[]
-               .soft_reset     = rtl822x_c45_soft_reset,
-               .get_features   = rtl822x_c45_get_features,
-               .config_aneg    = rtl822x_c45_config_aneg,
--              .config_init    = rtl822x_config_init,
-+              .config_init    = rtl8226_config_init,
-               .inband_caps    = rtl822x_inband_caps,
-               .config_inband  = rtl822x_config_inband,
-               .read_status    = rtl822xb_c45_read_status,
index d67d2eb2b4afe3274e778d41ba66650ac6d230bf..1fc138a4659f60f14cd71500ea639bf1b8750b1e 100644 (file)
@@ -42,7 +42,7 @@
  #define RTL8221B_PHYCR1                               0xa430
  #define RTL8221B_PHYCR1_ALDPS_EN              BIT(2)
  #define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN     BIT(12)
-@@ -2058,6 +2093,147 @@ exit:
+@@ -2069,6 +2104,147 @@ exit:
        return ret;
  }
  
  static int rtl8224_mdi_config_order(struct phy_device *phydev)
  {
        struct device_node *np = phydev->mdio.dev.of_node;
-@@ -2112,6 +2288,10 @@ static int rtl8224_config_init(struct ph
+@@ -2123,6 +2299,10 @@ static int rtl8224_config_init(struct ph
  {
        int ret;