]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
realtek: phy: add RTL8214FC initialization for RTL839x 21435/head
authorMarkus Stockhausen <markus.stockhausen@gmx.de>
Wed, 7 Jan 2026 07:32:58 +0000 (08:32 +0100)
committerStijn Tintel <stijn@linux-ipv6.be>
Mon, 12 Jan 2026 02:44:48 +0000 (04:44 +0200)
Until now the RTL8214FC is initialized either by U-Boot (all
devices) or by some magic firmware file (RTL838x). On RTL839x
devices without U-Boot (e.g. ZyXEL GS1920) this PHY cannot
be used.

Provide the most basic setup sequence for RTL839x so that
copper/fiber work. Later it can be taken over for all devices
and the firmware helpers can be dropped.

Remark! This should not (but might) break RTL839x devices with
RTL8214FC U-Boot setup.

Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/21435
Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c

index 8ac3024439d38d2409604ea43139c5b28e9aa7c1..046b9cc0d47b83698e4c35158b00f5d17cca0587 100644 (file)
@@ -811,20 +811,6 @@ static const struct sfp_upstream_ops rtl8214fc_sfp_ops = {
        .module_remove = rtl8214fc_sfp_remove,
 };
 
-static int rtl8214fc_phy_probe(struct phy_device *phydev)
-{
-       int ret = 0;
-
-       if (rtl821x_package_join(phydev, 4) == RTL821X_JOIN_LAST) {
-               if (soc_info.family == RTL8380_FAMILY_ID)
-                       ret = rtl8380_configure_rtl8214fc(get_base_phy(phydev));
-               if (ret)
-                       return ret;
-       }
-
-       return phy_sfp_probe(phydev, &rtl8214fc_sfp_ops);
-}
-
 static int rtl8214c_phy_probe(struct phy_device *phydev)
 {
        if (rtl821x_package_join(phydev, 4) == RTL821X_JOIN_LAST)
@@ -931,6 +917,58 @@ static int rtl8218b_config_init(struct phy_device *phydev)
        return 0;
 }
 
+static int rtl8214fc_config_init(struct phy_device *phydev)
+{
+       static int regs[] = {16, 19, 20, 21};
+       struct phy_device *portphy;
+       int port;
+
+       /* Hardware is similar to RTL8218B reuse coding for serdes and copper init */
+       rtl8218b_config_init(phydev);
+
+       if (phydev->mdio.addr % 8)
+               return 0;
+
+       for (port = 0; port < 4; port++) {
+               portphy = get_package_phy(phydev, port);
+
+               phy_write(phydev, RTL821XEXT_MEDIA_PAGE_SELECT, 0x8);
+               /* setup basic fiber control in base phy and default to copper */
+               phy_write_paged(phydev, 0x266, regs[port], 0x0f95);
+               phy_write(phydev, RTL821XEXT_MEDIA_PAGE_SELECT, 0x0);
+
+               phy_write(portphy, RTL821XEXT_MEDIA_PAGE_SELECT, 0x3);
+               /* set fiber SerDes RX to negative edge */
+               phy_modify_paged(portphy, 0x8, 0x17, 0, BIT(14));
+               /* auto negotiation disable link on */
+               phy_modify_paged(portphy, 0x8, 0x14, 0, BIT(2));
+               /* disable fiber 100MBit */
+               phy_modify_paged(portphy, 0x8, 0x11, BIT(5), 0);
+               phy_write(portphy, RTL821XEXT_MEDIA_PAGE_SELECT, 0x0);
+
+               /* Disable EEE. 0xa5d/0x10 is the same as MDIO_MMD_AN / MDIO_AN_EEE_ADV */
+               phy_write_paged(portphy, 0xa5d, 0x10, 0x0000);
+       }
+
+       return 0;
+}
+
+static int rtl8214fc_phy_probe(struct phy_device *phydev)
+{
+       int ret = 0;
+
+       if (rtl821x_package_join(phydev, 4) == RTL821X_JOIN_LAST) {
+               if (soc_info.family == RTL8380_FAMILY_ID)
+                       ret = rtl8380_configure_rtl8214fc(get_base_phy(phydev));
+               else if (soc_info.family == RTL8390_FAMILY_ID)
+                       ret = rtl8214fc_config_init(get_base_phy(phydev));
+               if (ret)
+                       return ret;
+       }
+
+       return phy_sfp_probe(phydev, &rtl8214fc_sfp_ops);
+}
+
 static struct phy_driver rtl83xx_phy_driver[] = {
        {
                PHY_ID_MATCH_EXACT(PHY_ID_RTL8214C),