The functionality/support for 5G and 10G PHYs was extracted from the realtek-phy driver and ported to the upstream Linux realtek PHY driver. These PHY chips need a sequence of register writes (and similar operations) for initialization. These sequences are provided as firmware files which are interpreted/applied by a new register patch engine. By switching to the upstream driver, it should be possible to get rid of a large chunk of (from OpenWrt perspective) unmaintained code from Realtek. The actual Linux phy-core infrastructure from Linux can be mostly used and only the Realtek specific quirks need to be handled. The files which need to be provided are depending on the PHY: * rtl8261n.bin (package "rtl8261n-firmware" or "rtl8261n-lp-firmware") - RTL8251L 5Gbps PHY - RTL8261BE 10Gbps PHY - RTL8261N 10Gbps PHY * rtl8264b.bin (package "rtl8264b-firmware") - RTL8254B 5Gbps PHY - RTL8264 10Gbps PHY - RTL8264B 10Gbps PHY Files which are affected by this change (DEVICE_PACKAGES dependencies, hwmon paths, default kernel configurations, refresh of patches, ...) are updated at the same times. Signed-off-by: Balázs Triszka <info@balika011.hu> Co-authored-by: Semih Baskan <strst.gs@gmail.com> Co-authored-by: Jonas Jelonek <jelonek.jonas@gmail.com> Co-authored-by: Gilly1970 <gilroyscott@hotmail.com> Co-authored-by: Aleksander Jan Bajkowski <olek2@wp.pl> Co-authored-by: Carlo Szelinsky <github@szelinsky.de> [sven: rebase, integrate suggestions from PR, add device packages, split] Signed-off-by: Sven Eckelmann <sven@narfation.org> [daniel: stripped to Linux 6.18 only, dropped unrelated changes] Signed-off-by: Daniel Golle <daniel@makrotopia.org>
191 lines
4.7 KiB
Diff
191 lines
4.7 KiB
Diff
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,
|