kernel: rtl8261n: add support for Serdes TX swap
authorChristian Marangi <[email protected]>
Sun, 28 Sep 2025 18:24:28 +0000 (20:24 +0200)
committerChristian Marangi <[email protected]>
Mon, 29 Sep 2025 10:00:22 +0000 (12:00 +0200)
Add support for swapping the Serdes TX line on RTL8261N PHYs.
This is used on an Arcadyan Mozart board where the Serdes TX is swapped
on the PHY (instead of on the Soc) to permit support of SFP module by
using toggling the integrated MUX.

Link: https://github.com/openwrt/openwrt/pull/20227
Signed-off-by: Christian Marangi <[email protected]>
target/linux/generic/files/drivers/net/phy/rtl8261n/rtk_phy.c
target/linux/generic/files/drivers/net/phy/rtl8261n/rtk_phylib.h

index 23916a89efb673e40c6d05a16bc8de863351e7da..7d2a38740dc7215925694fc51ff3055f83156ea0 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/module.h>
 #include <linux/phy.h>
+#include <linux/property.h>
 
 #include "phy_rtl826xb_patch.h"
 #include "rtk_phylib_rtl826xb.h"
 #define REALTEK_PHY_ID_RTL8264B         0x001CC813
 #define REALTEK_PHY_ID_RTL8264          0x001CCAF2
 
+#define REALTEK_SERDES_GLOBAL_CFG       0x1c
+#define   REALTEK_HSO_INV               BIT(7)
+#define   REALTEK_HSI_INV               BIT(6)
+
 static int rtl826xb_get_features(struct phy_device *phydev)
 {
     int ret;
@@ -41,6 +46,7 @@ static int rtl826xb_get_features(struct phy_device *phydev)
 
 static int rtl826xb_probe(struct phy_device *phydev)
 {
+    struct device *dev = &phydev->mdio.dev;
     struct rtk_phy_priv *priv = NULL;
 
     priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct rtk_phy_priv), GFP_KERNEL);
@@ -55,6 +61,7 @@ static int rtl826xb_probe(struct phy_device *phydev)
 
     priv->phytype = (phydev->drv->phy_id == REALTEK_PHY_ID_RTL8261N) ? (RTK_PHYLIB_RTL8261N) : (RTK_PHYLIB_RTL8264B);
     priv->isBasePort = (phydev->drv->phy_id == REALTEK_PHY_ID_RTL8261N) ? (1) : (((phydev->mdio.addr % 4) == 0) ? (1) : (0));
+    priv->pnswap_tx = device_property_read_bool(dev, "realtek,pnswap-tx");
     phydev->priv = priv;
 
     return 0;
@@ -62,6 +69,7 @@ static int rtl826xb_probe(struct phy_device *phydev)
 
 static int rtkphy_config_init(struct phy_device *phydev)
 {
+    struct rtk_phy_priv *priv = phydev->priv;
     int ret = 0;
     switch (phydev->drv->phy_id)
     {
@@ -117,6 +125,11 @@ static int rtkphy_config_init(struct phy_device *phydev)
             }
           #endif
 
+            if (priv->pnswap_tx)
+                phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
+                                 REALTEK_SERDES_GLOBAL_CFG,
+                                 REALTEK_HSO_INV);
+
             break;
         default:
             phydev_err(phydev, "%s:%u Unknow phy_id: 0x%X\n", __FUNCTION__, __LINE__, phydev->drv->phy_id);
index 70eb8e48edc90fe220d74831d3c44fad9069ed3f..c94e9cb9661873fcadb476c85003f521c10c95a9 100644 (file)
@@ -8,6 +8,8 @@
 #define __RTK_PHYLIB_H
 
 #if defined(RTK_PHYDRV_IN_LINUX)
+  #include <linux/types.h>
+
   #include "type.h"
   #include "rtk_phylib_def.h"
 #else
@@ -48,6 +50,8 @@ struct rtk_phy_priv {
     rtk_phylib_phy_t phytype;
     uint8 isBasePort;
     rt_phy_patch_db_t *patch;
+
+    bool pnswap_tx;
 };
 
 #if defined(RTK_PHYDRV_IN_LINUX)