From 6369c9e5c79994c380d0c63cfb003c935a974332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Triszka?= Date: Wed, 1 Apr 2026 15:32:17 +0200 Subject: [PATCH] generic: net: phy: realtek: add 5G and 10G PHY support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Co-authored-by: Semih Baskan Co-authored-by: Jonas Jelonek Co-authored-by: Gilly1970 Co-authored-by: Aleksander Jan Bajkowski Co-authored-by: Carlo Szelinsky [sven: rebase, integrate suggestions from PR, add device packages, split] Signed-off-by: Sven Eckelmann [daniel: stripped to Linux 6.18 only, dropped unrelated changes] Signed-off-by: Daniel Golle --- ...y-realtek-add-5G-and-10G-PHY-support.patch | 1219 +++++++++++++++++ target/linux/mediatek/filogic/config-6.18 | 1 - target/linux/mediatek/image/filogic.mk | 6 +- target/linux/realtek/image/rtl930x.mk | 5 +- ...altek-add-RTL8224-pair-order-support.patch | 12 +- ...realtek-add-RTL8224-polarity-support.patch | 4 +- .../patches-6.18/720-add-rtl-phy.patch | 6 +- ...tek-support-MDI-swapping-for-RTL8226.patch | 4 +- ...743-net-realtek-serdes-configuration.patch | 6 +- target/linux/realtek/rtl930x/config-6.18 | 1 - target/linux/realtek/rtl930x_nand/config-6.18 | 1 - 11 files changed, 1243 insertions(+), 22 deletions(-) create mode 100644 target/linux/generic/pending-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch diff --git a/target/linux/generic/pending-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch b/target/linux/generic/pending-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch new file mode 100644 index 00000000000..71b025eefe6 --- /dev/null +++ b/target/linux/generic/pending-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch @@ -0,0 +1,1219 @@ +From: Balázs Triszka +Date: Sat, 18 Oct 2025 23:09:23 +0200 +Subject: net: phy: realtek: add 5G and 10G PHY support + +The functionality 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 for initialization. +These are provided as firmware files which are interpreted/applied by a +new register patch engine. + +The files which need to be provided are depending on the PHY: + +* rtl8261n.bin + - RTL8251L 5Gbps PHY + - RTL8261BE 10Gbps PHY + - RTL8261N 10Gbps PHY +* rtl8264b.bin + - RTL8254B 5Gbps PHY + - RTL8264 10Gbps PHY + - RTL8264B 10Gbps PHY + +Signed-off-by: Balázs Triszka + +--- a/drivers/net/phy/realtek/Makefile ++++ b/drivers/net/phy/realtek/Makefile +@@ -1,4 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0 + realtek-y += realtek_main.o ++realtek-y += phy_patch.o ++realtek-y += phy_patch_rtl826x.o + realtek-$(CONFIG_REALTEK_PHY_HWMON) += realtek_hwmon.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o +--- a/drivers/net/phy/realtek/realtek.h ++++ b/drivers/net/phy/realtek/realtek.h +@@ -5,6 +5,14 @@ + + #include + ++#include "phy_patch.h" ++#include "phy_patch_rtl826x.h" ++ ++struct rtl826x_priv { ++ struct rtlgen_phy_patch_db *patch; ++ bool enable_pma_low_power:1; ++}; ++ + int rtl822x_hwmon_init(struct phy_device *phydev); + + #endif /* REALTEK_H */ +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -11,11 +11,13 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + +@@ -205,10 +207,26 @@ + #define RTL_8221B_VM_CG 0x001cc84a + #define RTL_8251B 0x001cc862 + #define RTL_8261C 0x001cc890 ++#define RTL_8261N 0x001ccaf3 ++#define RTL_8264 0x001ccaf2 ++#define RTL_8264B 0x001cc813 + + /* RTL8211E and RTL8211F support up to three LEDs */ + #define RTL8211x_LED_COUNT 3 + ++#define RTL826X_VEND1_SERDES_GLOBAL_CFG 0xc1 ++#define RTL826X_VEND1_SERDES_GLOBAL_CFG_HSI_INV BIT(6) ++#define RTL826X_VEND1_SERDES_GLOBAL_CFG_HSO_INV BIT(7) ++ ++#define RTL826X_VEND1_PKG_MODEL 0x103 ++#define RTL826X_VEND1_VERSION_ID 0x104 ++ ++#define RTL826X_VND2_INER 0xA424 ++#define RTL826X_VND2_INER_LINK_STATUS BIT(4) ++#define RTL826X_VND2_INER_PME BIT(7) ++ ++#define RTL826X_VND2_INSR 0xA43A ++ + MODULE_DESCRIPTION("Realtek PHY driver"); + MODULE_AUTHOR("Johnson Leung"); + MODULE_LICENSE("GPL"); +@@ -1993,6 +2011,125 @@ static int rtl8251b_c45_match_phy_device + return rtlgen_is_c45_match(phydev, RTL_8251B, true); + } + ++static int rtl8251l_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ int data; ++ ++ if (!rtlgen_is_c45_match(phydev, RTL_8261N, true)) ++ return 0; ++ ++ if (!phydev->mdio.bus->read_c45) ++ return 0; ++ ++ data = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_PKG_MODEL); ++ if (data < 0) ++ return 0; ++ ++ if (data != 0x8251) ++ return 0; ++ ++ return 1; ++} ++ ++static int rtl8254b_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ int data; ++ ++ if (!rtlgen_is_c45_match(phydev, RTL_8264B, true)) ++ return 0; ++ ++ if (!phydev->mdio.bus->read_c45) ++ return 0; ++ ++ data = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_PKG_MODEL); ++ if (data < 0) ++ return 0; ++ ++ if (data != 0x8254) ++ return 0; ++ ++ return 1; ++} ++ ++static int rtl8261be_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ int data; ++ ++ if (!rtlgen_is_c45_match(phydev, RTL_8261N, true)) ++ return 0; ++ ++ if (!phydev->mdio.bus->read_c45) ++ return 0; ++ ++ data = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_PKG_MODEL); ++ if (data < 0) ++ return 0; ++ ++ if (data == 0x8251) ++ return 0; ++ ++ data = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_VERSION_ID); ++ if (data < 0) ++ return 0; ++ ++ if ((data & 0xFFC0) != 0x1140) ++ return 0; ++ ++ return 1; ++} ++ ++static int rtl8261n_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ int data; ++ ++ if (!rtlgen_is_c45_match(phydev, RTL_8261N, true)) ++ return 0; ++ ++ if (!phydev->mdio.bus->read_c45) ++ return 0; ++ ++ data = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_PKG_MODEL); ++ if (data < 0) ++ return 0; ++ ++ if (data == 0x8251) ++ return 0; ++ ++ data = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_VERSION_ID); ++ if (data < 0) ++ return 0; ++ ++ if ((data & 0xFFC0) == 0x1140) ++ return 0; ++ ++ return 1; ++} ++ ++static int rtl8264b_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ int data; ++ ++ if (!rtlgen_is_c45_match(phydev, RTL_8264B, true)) ++ return 0; ++ ++ if (!phydev->mdio.bus->read_c45) ++ return 0; ++ ++ data = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_PKG_MODEL); ++ if (data < 0) ++ return 0; ++ ++ if (data == 0x8254) ++ return 0; ++ ++ return 1; ++} ++ + static int rtlgen_resume(struct phy_device *phydev) + { + int ret = genphy_resume(phydev); +@@ -2214,6 +2351,493 @@ static int rtlgen_sfp_config_aneg(struct + return 0; + } + ++static int rtl826x_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct rtl826x_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(struct rtl826x_priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->enable_pma_low_power = device_property_read_bool(dev, "realtek,enable-pma-low-power"); ++ phydev->priv = priv; ++ ++ if (!priv->enable_pma_low_power) ++ phydev_warn(phydev, "PMA low-power suspend disabled\n"); ++ ++ /* Disable EEE due to link stability issues */ ++ phy_disable_eee_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Full_BIT); ++ phy_disable_eee_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Full_BIT); ++ ++ if (IS_ENABLED(CONFIG_REALTEK_PHY_HWMON)) ++ return rtl822x_hwmon_init(phydev); ++ ++ return 0; ++} ++ ++static int rtl8261n_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = rtl826x_probe(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = rtl8261n_phy_patch_db_init(phydev); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int rtl8264b_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = rtl826x_probe(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = rtl8264b_phy_patch_db_init(phydev); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int rtl826x_config_serdes_polarity(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ unsigned int pol; ++ u16 mask = 0; ++ u16 set = 0; ++ int ret; ++ ++ ret = phy_get_rx_polarity(dev_fwnode(dev), phy_modes(phydev->interface), ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ PHY_POL_NORMAL, &pol); ++ if (ret) ++ return ret; ++ ++ mask |= RTL826X_VEND1_SERDES_GLOBAL_CFG_HSI_INV; ++ if (pol == PHY_POL_INVERT) ++ set |= RTL826X_VEND1_SERDES_GLOBAL_CFG_HSI_INV; ++ ++ ret = phy_get_tx_polarity(dev_fwnode(dev), phy_modes(phydev->interface), ++ BIT(PHY_POL_NORMAL) | BIT(PHY_POL_INVERT), ++ PHY_POL_NORMAL, &pol); ++ if (ret) ++ return ret; ++ ++ mask |= RTL826X_VEND1_SERDES_GLOBAL_CFG_HSO_INV; ++ if (pol == PHY_POL_INVERT) ++ set |= RTL826X_VEND1_SERDES_GLOBAL_CFG_HSO_INV; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, RTL826X_VEND1_SERDES_GLOBAL_CFG, ++ mask, set); ++} ++ ++static int rtl826x_config_init(struct phy_device *phydev) ++{ ++ const unsigned int max_dereset_tries = 5; ++ unsigned int tries = 0; ++ int ret; ++ ++ /* Suppress PHY interrupt output before the hardware reset below. ++ * The RTL8261N asserts its interrupt pin during power-on reset, which ++ * races with phylib registering phy_interrupt. If the IRQ fires first ++ * the kernel disables it permanently. rtl826x_config_intr() will ++ * re-enable the interrupt correctly once phylib is ready. ++ */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL826X_VND2_INER, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xE1, BIT(0)); ++ if (ret < 0) ++ return ret; ++ ++ /* toggle reset */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, 0x145, BIT(0)); ++ if (ret < 0) ++ return ret; ++ ++ do { ++ msleep(30); ++ tries++; ++ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0x145, BIT(0)); ++ } while (ret < 0 && tries <= max_dereset_tries); ++ ++ if (ret < 0) { ++ phydev_err(phydev, "deassert reset failed %d\n", ret); ++ return ret; ++ } ++ ++ msleep(30); ++ ++ ret = rtlgen_phy_patch(phydev); ++ if (ret) { ++ phydev_err(phydev, "patch failed!! %d\n", ret); ++ return ret; ++ } ++ ++ return rtl826x_config_serdes_polarity(phydev); ++} ++ ++static int rtl826x_suspend(struct phy_device *phydev) ++{ ++ struct rtl826x_priv *priv = phydev->priv; ++ ++ if (!priv->enable_pma_low_power) ++ return -EOPNOTSUPP; ++ ++ return genphy_c45_pma_suspend(phydev); ++} ++ ++static int rtl826x_get_features(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_c45_pma_read_abilities(phydev); ++ if (ret) ++ return ret; ++ ++ /* not support 10M modes */ ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ phydev->supported); ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ phydev->supported); ++ ++ return 0; ++} ++ ++static int rtl825xb_get_features(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_c45_pma_read_abilities(phydev); ++ if (ret) ++ return ret; ++ ++ /* not support 10M modes */ ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, ++ phydev->supported); ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, ++ phydev->supported); ++ ++ /* faulty rtl826x silicon having issues with 10G, sold as only 5G phy */ ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ++ phydev->supported); ++ ++ return 0; ++} ++ ++static int rtl826x_config_aneg(struct phy_device *phydev) ++{ ++ bool changed = false; ++ u16 reg; ++ int ret; ++ ++ phydev->mdix_ctrl = ETH_TP_MDI_AUTO; ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ return genphy_c45_pma_setup_forced(phydev); ++ ++ ret = genphy_c45_an_config_aneg(phydev); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ reg = 0; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->advertising)) ++ reg |= ADVERTISE_1000FULL; ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ phydev->advertising)) ++ reg |= ADVERTISE_1000HALF; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, ++ RTL822X_VND2_C22_REG(MII_CTRL1000), ++ ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ return genphy_c45_check_and_restart_aneg(phydev, changed); ++} ++ ++static int rtl826x_read_status(struct phy_device *phydev) ++{ ++ int status; ++ int ret; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ ret = genphy_c45_read_link(phydev); ++ if (ret) ++ return ret; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ ret = genphy_c45_read_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ status = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_C22_REG(MII_STAT1000)); ++ if (status < 0) ++ return status; ++ ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, status); ++ ++ phy_resolve_aneg_linkmode(phydev); ++ } else { ++ ret = genphy_c45_read_pma(phydev); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return genphy_c45_read_mdix(phydev); ++} ++ ++static int rtl826x_config_intr(struct phy_device *phydev) ++{ ++ u16 interrupts; ++ int ret; ++ ++ /* Disable all IMR */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xE1, 0); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xE3, 0); ++ if (ret < 0) ++ return ret; ++ ++ /* source */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xE4, 0x1); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xE0, 0x2F); ++ if (ret < 0) ++ return ret; ++ ++ /* init common link change & WOL */ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) ++ interrupts = RTL826X_VND2_INER_LINK_STATUS | RTL826X_VND2_INER_PME; ++ else ++ interrupts = 0; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL826X_VND2_INER, interrupts); ++ if (ret < 0) ++ return ret; ++ ++ /* clear status */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x2DC, 0xFF); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, 0xE1, BIT(0), ++ phydev->interrupts == PHY_INTERRUPT_ENABLED); ++} ++ ++static irqreturn_t rtl826x_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_enabled; ++ int irq_status; ++ ++ irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL826X_VND2_INSR); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x2DC, 0xFF) < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ irq_enabled = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL826X_VND2_INER); ++ if (irq_enabled < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (!(irq_status & irq_enabled)) ++ return IRQ_NONE; ++ ++ if (irq_status & RTL826X_VND2_INER_LINK_STATUS) { ++ phydev_dbg(phydev, "RTK_PHY_INTR_LINK_CHANGE\n"); ++ phy_mac_interrupt(phydev); ++ } ++ ++ if (irq_status & RTL826X_VND2_INER_PME) { ++ phydev_dbg(phydev, "RTK_PHY_INTR_WOL\n"); ++ ++ if (phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, 0xD8A2, BIT(15)) < 0) ++ return IRQ_NONE; ++ ++ if (phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, 0xD8A2, BIT(15)) < 0) ++ return IRQ_NONE; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int rtl826x_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) ++{ ++ static const u32 cfg_reg[4] = {0xD8C6, 0xD8C8, 0xD8CA, 0xD8CC}; ++ struct net_device *ndev = phydev->attached_dev; ++ struct netdev_hw_addr *ha; ++ u16 rtk_wolopts; ++ size_t idx; ++ u32 offset; ++ int ret; ++ ++ if (!ndev) ++ return -EINVAL; ++ ++ if (wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) ++ return -EOPNOTSUPP; ++ ++ if (wol->wolopts & (WAKE_MAGIC | WAKE_UCAST)) { ++ const u8 *mac_addr = ndev->dev_addr; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xD8C0, ++ mac_addr[1] << 8 | mac_addr[0]); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xD8C2, ++ mac_addr[3] << 8 | mac_addr[2]); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xD8C4, ++ mac_addr[5] << 8 | mac_addr[4]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (wol->wolopts & WAKE_MCAST) { ++ for (idx = 0; idx < ARRAY_SIZE(cfg_reg); idx++) { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, cfg_reg[idx], 0); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (!netdev_mc_empty(ndev)) { ++ netdev_for_each_mc_addr(ha, ndev) { ++ phydev_dbg(phydev, "mac: %pM\n", ha->addr); ++ ++ offset = crc32_be(~0, ha->addr, 6) >> 26; ++ idx = offset >> 4; ++ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, cfg_reg[idx], ++ BIT(offset & 0xF)); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ } ++ ++ rtk_wolopts = 0; ++ if (wol->wolopts & WAKE_PHY) ++ rtk_wolopts |= BIT(13); ++ if (wol->wolopts & WAKE_MAGIC) ++ rtk_wolopts |= BIT(12); ++ if (wol->wolopts & WAKE_UCAST) ++ rtk_wolopts |= BIT(10); ++ if (wol->wolopts & WAKE_MCAST) ++ rtk_wolopts |= BIT(9); ++ if (wol->wolopts & WAKE_BCAST) ++ rtk_wolopts |= BIT(8); ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xD8A0, ++ GENMASK(13, 12) | GENMASK(10, 8), rtk_wolopts); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static void rtl826x_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) ++{ ++ int rtk_wolopts; ++ ++ wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST; ++ wol->wolopts = 0; ++ ++ rtk_wolopts = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xD8A0); ++ if (rtk_wolopts < 0) ++ return; ++ ++ if (rtk_wolopts & BIT(13)) ++ wol->wolopts |= WAKE_PHY; ++ if (rtk_wolopts & BIT(12)) ++ wol->wolopts |= WAKE_MAGIC; ++ if (rtk_wolopts & BIT(10)) ++ wol->wolopts |= WAKE_UCAST; ++ if (rtk_wolopts & BIT(9)) ++ wol->wolopts |= WAKE_MCAST; ++ if (rtk_wolopts & BIT(8)) ++ wol->wolopts |= WAKE_BCAST; ++} ++ ++static int rtl826x_get_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, void *data) ++{ ++ int val; ++ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_EDPD: ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_PHYCR1); ++ if (val < 0) ++ return val; ++ ++ *(u16 *)data = (!(val & RTL8221B_PHYCR1_ALDPS_EN)) ? ETHTOOL_PHY_EDPD_DISABLE : ++ ETHTOOL_PHY_EDPD_DFLT_TX_MSECS; ++ break; ++ ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int rtl826x_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data) ++{ ++ int ret; ++ u16 val; ++ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_EDPD: ++ switch (*(const u16 *)data) { ++ case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: ++ val = RTL8221B_PHYCR1_ALDPS_EN; ++ break; ++ case ETHTOOL_PHY_EDPD_DISABLE: ++ val = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_PHYCR1, ++ RTL8221B_PHYCR1_ALDPS_EN, val); ++ if (ret < 0) ++ return ret; ++ break; ++ ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ + static struct phy_driver realtek_drvs[] = { + { + PHY_ID_MATCH_EXACT(0x00008201), +@@ -2520,6 +3144,108 @@ static struct phy_driver realtek_drvs[] + .resume = genphy_resume, + .read_mmd = genphy_read_mmd_unsupported, + .write_mmd = genphy_write_mmd_unsupported, ++ }, { ++ .name = "RTL8251L 5Gbps PHY", ++ .config_init = rtl826x_config_init, ++ .probe = rtl8261n_probe, ++ .get_features = rtl825xb_get_features, ++ .suspend = rtl826x_suspend, ++ .resume = rtlgen_c45_resume, ++ .config_aneg = rtl826x_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = rtl826x_read_status, ++ .config_intr = rtl826x_config_intr, ++ .handle_interrupt = rtl826x_handle_interrupt, ++ .match_phy_device = rtl8251l_match_phy_device, ++ .set_wol = rtl826x_set_wol, ++ .get_wol = rtl826x_get_wol, ++ .get_tunable = rtl826x_get_tunable, ++ .set_tunable = rtl826x_set_tunable, ++ }, { ++ .name = "RTL8254B 5Gbps PHY", ++ .config_init = rtl826x_config_init, ++ .probe = rtl8264b_probe, ++ .get_features = rtl825xb_get_features, ++ .suspend = rtl826x_suspend, ++ .resume = rtlgen_c45_resume, ++ .config_aneg = rtl826x_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = rtl826x_read_status, ++ .config_intr = rtl826x_config_intr, ++ .handle_interrupt = rtl826x_handle_interrupt, ++ .match_phy_device = rtl8254b_match_phy_device, ++ .set_wol = rtl826x_set_wol, ++ .get_wol = rtl826x_get_wol, ++ .get_tunable = rtl826x_get_tunable, ++ .set_tunable = rtl826x_set_tunable, ++ }, { ++ .name = "RTL8261BE 10Gbps PHY", ++ .config_init = rtl826x_config_init, ++ .probe = rtl8261n_probe, ++ .get_features = rtl826x_get_features, ++ .suspend = rtl826x_suspend, ++ .resume = rtlgen_c45_resume, ++ .config_aneg = rtl826x_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = rtl826x_read_status, ++ .config_intr = rtl826x_config_intr, ++ .handle_interrupt = rtl826x_handle_interrupt, ++ .match_phy_device = rtl8261be_match_phy_device, ++ .set_wol = rtl826x_set_wol, ++ .get_wol = rtl826x_get_wol, ++ .get_tunable = rtl826x_get_tunable, ++ .set_tunable = rtl826x_set_tunable, ++ }, { ++ .name = "RTL8261N 10Gbps PHY", ++ .config_init = rtl826x_config_init, ++ .probe = rtl8261n_probe, ++ .get_features = rtl826x_get_features, ++ .suspend = rtl826x_suspend, ++ .resume = rtlgen_c45_resume, ++ .config_aneg = rtl826x_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = rtl826x_read_status, ++ .config_intr = rtl826x_config_intr, ++ .handle_interrupt = rtl826x_handle_interrupt, ++ .match_phy_device = rtl8261n_match_phy_device, ++ .set_wol = rtl826x_set_wol, ++ .get_wol = rtl826x_get_wol, ++ .get_tunable = rtl826x_get_tunable, ++ .set_tunable = rtl826x_set_tunable, ++ }, { ++ PHY_ID_MATCH_EXACT(RTL_8264), ++ .name = "RTL8264 10Gbps PHY", ++ .config_init = rtl826x_config_init, ++ .probe = rtl8264b_probe, ++ .get_features = rtl826x_get_features, ++ .suspend = rtl826x_suspend, ++ .resume = rtlgen_c45_resume, ++ .config_aneg = rtl826x_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = rtl826x_read_status, ++ .config_intr = rtl826x_config_intr, ++ .handle_interrupt = rtl826x_handle_interrupt, ++ .set_wol = rtl826x_set_wol, ++ .get_wol = rtl826x_get_wol, ++ .get_tunable = rtl826x_get_tunable, ++ .set_tunable = rtl826x_set_tunable, ++ }, { ++ .name = "RTL8264B 10Gbps PHY", ++ .config_init = rtl826x_config_init, ++ .probe = rtl8264b_probe, ++ .get_features = rtl826x_get_features, ++ .suspend = rtl826x_suspend, ++ .resume = rtlgen_c45_resume, ++ .config_aneg = rtl826x_config_aneg, ++ .aneg_done = genphy_c45_aneg_done, ++ .read_status = rtl826x_read_status, ++ .config_intr = rtl826x_config_intr, ++ .handle_interrupt = rtl826x_handle_interrupt, ++ .match_phy_device = rtl8264b_match_phy_device, ++ .set_wol = rtl826x_set_wol, ++ .get_wol = rtl826x_get_wol, ++ .get_tunable = rtl826x_get_tunable, ++ .set_tunable = rtl826x_set_tunable, + }, + }; + +--- a/drivers/net/phy/realtek/Kconfig ++++ b/drivers/net/phy/realtek/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config REALTEK_PHY + tristate "Realtek PHYs" ++ select PHY_COMMON_PROPS + help + Currently supports RTL821x/RTL822x and fast ethernet PHYs + +--- /dev/null ++++ b/drivers/net/phy/realtek/phy_patch.c +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include "phy_patch.h" ++ ++#include ++#include ++ ++#include "realtek.h" ++ ++int rtlgen_phy_patch(struct phy_device *phydev) ++{ ++ struct rtl826x_priv *priv = phydev->priv; ++ struct rtlgen_phy_patch_db *patch_db = priv->patch; ++ size_t i; ++ int ret; ++ ++ if (!patch_db || !patch_db->patch_op_exec) { ++ phydev_err(phydev, "patch db or exec callback is NULL\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < patch_db->count; i++) { ++ ret = patch_db->patch_op_exec(phydev, &patch_db->conf[i]); ++ if (ret < 0) { ++ phydev_err(phydev, "patch failed! %zu[%u][0x%X][0x%X][0x%X] ret=%d\n", ++ i, patch_db->conf[i].patch_op, ++ le16_to_cpu(patch_db->conf[i].pagemmd), ++ le16_to_cpu(patch_db->conf[i].addr), ++ le16_to_cpu(patch_db->conf[i].data), ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} +--- /dev/null ++++ b/drivers/net/phy/realtek/phy_patch.h +@@ -0,0 +1,42 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef __PHY_PATCH_H__ ++#define __PHY_PATCH_H__ ++ ++#include ++#include ++ ++enum rtlgen_phypatch_op { ++ RTK_PATCH_OP_PHY, ++ RTK_PATCH_OP_PHY_WAIT, ++ RTK_PATCH_OP_PHY_WAIT_NOT, ++ RTK_PATCH_OP_PHYOCP, ++ RTK_PATCH_OP_PHYOCP_BC62, ++ RTK_PATCH_OP_TOP, ++ RTK_PATCH_OP_TOPOCP, // TODO unimplemented ++ RTK_PATCH_OP_PSDS0, ++ RTK_PATCH_OP_PSDS1, // TODO unimplemented ++ RTK_PATCH_OP_MSDS, // TODO unimplemented ++ RTK_PATCH_OP_MAC, // TODO unimplemented ++ RTK_PATCH_OP_DELAY_MS ++}; ++ ++struct rtlgen_hwpatch { ++ u8 patch_op; ++ u8 portmask; ++ __le16 pagemmd; ++ __le16 addr; ++ u8 msb; ++ u8 lsb; ++ __le16 data; ++} __packed; ++ ++struct rtlgen_phy_patch_db { ++ int (*patch_op_exec)(struct phy_device *phydev, const struct rtlgen_hwpatch *patch); ++ size_t count; ++ struct rtlgen_hwpatch conf[] __counted_by(count); ++}; ++ ++int rtlgen_phy_patch(struct phy_device *phydev); ++ ++#endif +--- /dev/null ++++ b/drivers/net/phy/realtek/phy_patch_rtl826x.c +@@ -0,0 +1,287 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include "phy_patch_rtl826x.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "phy_patch.h" ++#include "realtek.h" ++ ++#define PHY_PATCH_WAIT_TIMEOUT 10000000 ++ ++static u16 rtl826x_mmd_convert(u16 page, u16 addr) ++{ ++ if (addr < 16) ++ return 0xA400 + (page * 2); ++ ++ if (addr < 24) ++ return (16 * page) + ((addr - 16) * 2); ++ ++ return 0xA430 + ((addr - 24) * 2); ++} ++ ++static int rtl826x_phy_patch_wait(struct phy_device *phydev, u32 mmd_addr, u32 mmd_reg, ++ u16 data, u16 mask, bool wait_equal) ++{ ++ s64 us_diff = 0; ++ ktime_t start; ++ ktime_t now; ++ bool equal; ++ int ret; ++ ++ start = ktime_get(); ++ ++ do { ++ ret = phy_read_mmd(phydev, mmd_addr, mmd_reg); ++ if (ret < 0) ++ return ret; ++ ++ equal = (ret & mask) == data; ++ if (equal == wait_equal) ++ break; ++ ++ msleep(1); ++ ++ now = ktime_get(); ++ us_diff = ktime_to_us(ktime_sub(now, start)); ++ } while (us_diff < PHY_PATCH_WAIT_TIMEOUT); ++ ++ if (us_diff >= PHY_PATCH_WAIT_TIMEOUT) { ++ phydev_err(phydev, "826xb patch wait[%u,0x%X,0x%X,0x%X]:0x%X\n", ++ mmd_addr, mmd_reg, mask, data, ret); ++ return -ETIME; ++ } ++ ++ return 0; ++} ++ ++static int rtl826x_phy_patch_sds_get(struct phy_device *phydev, u32 sds_page, u32 sds_reg) ++{ ++ u32 sds_addr = 0x8000 + (sds_reg << 6) + sds_page; ++ int ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x143, sds_addr); ++ if (ret < 0) ++ return ret; ++ ++ ret = rtl826x_phy_patch_wait(phydev, MDIO_MMD_VEND1, 0x143, 0, BIT(15), true); ++ if (ret < 0) ++ return ret; ++ ++ return phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x142); ++} ++ ++static int rtl826x_phy_patch_sds_set(struct phy_device *phydev, u32 sds_page, u32 sds_reg, ++ u32 data) ++{ ++ u32 sds_addr = 0x8800 + (sds_reg << 6) + sds_page; ++ int ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x141, data); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x143, sds_addr); ++ if (ret < 0) ++ return ret; ++ ++ return rtl826x_phy_patch_wait(phydev, MDIO_MMD_VEND1, 0x143, 0, BIT(15), true); ++} ++ ++static int rtl826x_phy_patch_sds_modify(struct phy_device *phydev, u32 sds_page, u32 sds_reg, ++ u32 mask, u32 data) ++{ ++ int ret = 0; ++ ++ if (mask != GENMASK(15, 0)) { ++ ret = rtl826x_phy_patch_sds_get(phydev, sds_page, sds_reg); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return rtl826x_phy_patch_sds_set(phydev, sds_page, sds_reg, (ret & ~mask) | data); ++} ++ ++static int rtl826x_phy_patch_op(struct phy_device *phydev, const struct rtlgen_hwpatch *patch) ++{ ++ u32 mask = GENMASK(patch->msb, patch->lsb); ++ u16 pagemmd = le16_to_cpu(patch->pagemmd); ++ u16 addr = le16_to_cpu(patch->addr); ++ u16 data = le16_to_cpu(patch->data); ++ bool wait_equal; ++ int ret = 0; ++ int val; ++ int cnt; ++ u16 reg; ++ ++ phydev_dbg(phydev, "patch: op: %u pm: %x mmd: %x addr: %x mask: [%u, %u] data: %x\n", ++ patch->patch_op, patch->portmask, pagemmd, addr, ++ patch->msb, patch->lsb, data); ++ ++ switch (patch->patch_op) { ++ case RTK_PATCH_OP_PHY: ++ reg = rtl826x_mmd_convert(pagemmd, addr); ++ if (mask != GENMASK(15, 0)) { ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, reg, mask, ++ data << patch->lsb); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, reg, ++ data << patch->lsb); ++ if (ret < 0) ++ return ret; ++ } ++ break; ++ ++ case RTK_PATCH_OP_PHY_WAIT: ++ case RTK_PATCH_OP_PHY_WAIT_NOT: ++ wait_equal = patch->patch_op == RTK_PATCH_OP_PHY_WAIT; ++ reg = rtl826x_mmd_convert(pagemmd, addr); ++ ret = rtl826x_phy_patch_wait(phydev, MDIO_MMD_VEND2, reg, ++ data << patch->lsb, mask, wait_equal); ++ if (ret < 0) ++ return ret; ++ break; ++ ++ case RTK_PATCH_OP_PHYOCP: ++ if (mask != GENMASK(15, 0)) { ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, addr, mask, ++ data << patch->lsb); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, addr, ++ data << patch->lsb); ++ if (ret < 0) ++ return ret; ++ } ++ break; ++ ++ case RTK_PATCH_OP_PHYOCP_BC62: ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xbc62); ++ if (val < 0) ++ return val; ++ ++ val = (val >> 8) & 0x1f; ++ for (cnt = 0; cnt <= val; cnt++) { ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xbc62, ++ GENMASK(12, 8), cnt << 8); ++ if (ret < 0) ++ return ret; ++ } ++ break; ++ ++ case RTK_PATCH_OP_TOP: ++ if (addr < 16) ++ return -EINVAL; ++ ++ reg = (pagemmd * 8) + (addr - 16); ++ if (mask != GENMASK(15, 0)) { ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, reg, mask, ++ data << patch->lsb); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, data << patch->lsb); ++ if (ret < 0) ++ return ret; ++ } ++ break; ++ ++ case RTK_PATCH_OP_PSDS0: ++ ret = rtl826x_phy_patch_sds_modify(phydev, pagemmd, addr, ++ mask, data << patch->lsb); ++ if (ret < 0) ++ return ret; ++ break; ++ ++ case RTK_PATCH_OP_DELAY_MS: ++ msleep(data); ++ break; ++ ++ default: ++ phydev_err(phydev, "%s: %u not implemented yet!\n", __func__, patch->patch_op); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int rtl826x_phy_patch_db_init(struct phy_device *phydev, const char *fw_name) ++{ ++ struct rtl826x_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ struct rtlgen_phy_patch_db *patch_db; ++ struct rtlgen_hwpatch *patches; ++ const struct firmware *fw; ++ size_t count; ++ int ret; ++ ++ ret = request_firmware_direct(&fw, fw_name, dev); ++ if (ret) { ++ if (ret == -ENOENT) { ++ phydev_dbg(phydev, "Failed to request %s: defer probing\n", fw_name); ++ return -EPROBE_DEFER; ++ } else { ++ phydev_err(phydev, "Failed to request %s: %d\n", fw_name, ret); ++ return ret; ++ } ++ } ++ ++ if (!fw->size || fw->size % sizeof(*patches) != 0) { ++ phydev_err(phydev, "Invalid firmware size %zu for %s\n", ++ fw->size, fw_name); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ patches = (struct rtlgen_hwpatch *)fw->data; ++ count = fw->size / sizeof(*patches); ++ ++ patch_db = devm_kzalloc(dev, struct_size(patch_db, conf, count), GFP_KERNEL); ++ if (!patch_db) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ patch_db->count = count; ++ patch_db->patch_op_exec = rtl826x_phy_patch_op; ++ ++ memcpy(patch_db->conf, patches, flex_array_size(patch_db, conf, count)); ++ ++ priv->patch = patch_db; ++ ++out: ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++int rtl8261n_phy_patch_db_init(struct phy_device *phydev) ++{ ++ return rtl826x_phy_patch_db_init(phydev, "rtl8261n.bin"); ++} ++ ++int rtl8264b_phy_patch_db_init(struct phy_device *phydev) ++{ ++ return rtl826x_phy_patch_db_init(phydev, "rtl8264b.bin"); ++} ++ ++MODULE_FIRMWARE("rtl8261n.bin"); ++MODULE_FIRMWARE("rtl8264b.bin"); +--- /dev/null ++++ b/drivers/net/phy/realtek/phy_patch_rtl826x.h +@@ -0,0 +1,11 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef __PHY_PATCH_RTL826X_H__ ++#define __PHY_PATCH_RTL826X_H__ ++ ++#include ++ ++int rtl8261n_phy_patch_db_init(struct phy_device *phydev); ++int rtl8264b_phy_patch_db_init(struct phy_device *phydev); ++ ++#endif diff --git a/target/linux/mediatek/filogic/config-6.18 b/target/linux/mediatek/filogic/config-6.18 index 30f1496c748..aefb9d9a075 100644 --- a/target/linux/mediatek/filogic/config-6.18 +++ b/target/linux/mediatek/filogic/config-6.18 @@ -471,7 +471,6 @@ CONFIG_RPS=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_MT7622=y CONFIG_RTC_I2C_AND_SPI=y -CONFIG_RTL8261N_PHY=y # CONFIG_RTL8367S_GSW is not set CONFIG_RWSEM_SPIN_ON_OWNER=y CONFIG_SCHED_MC=y diff --git a/target/linux/mediatek/image/filogic.mk b/target/linux/mediatek/image/filogic.mk index 84fed76bc1b..31713fd9293 100644 --- a/target/linux/mediatek/image/filogic.mk +++ b/target/linux/mediatek/image/filogic.mk @@ -2073,7 +2073,8 @@ TARGET_DEVICES += keenetic_kap-630 define Device/keenetic_kn-1812-common DEVICE_DTS_DIR := ../dts DEVICE_PACKAGES := kmod-mt7992-firmware kmod-usb3 \ - mt7988-2p5g-phy-firmware mt7988-wo-firmware + mt7988-2p5g-phy-firmware mt7988-wo-firmware \ + kmod-phy-realtek rtl8261n-firmware UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k PAGESIZE := 2048 @@ -3049,7 +3050,8 @@ define Device/tplink_be450 DEVICE_DTS := mt7988d-tplink-be450 DEVICE_DTS_DIR := ../dts DEVICE_PACKAGES := kmod-mt7992-firmware kmod-usb3 \ - mt7988-2p5g-phy-firmware mt7988-wo-firmware + mt7988-2p5g-phy-firmware mt7988-wo-firmware \ + kmod-phy-realtek rtl8261n-firmware UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k PAGESIZE := 2048 diff --git a/target/linux/realtek/image/rtl930x.mk b/target/linux/realtek/image/rtl930x.mk index acf8a2582d8..7e78b63ff9a 100644 --- a/target/linux/realtek/image/rtl930x.mk +++ b/target/linux/realtek/image/rtl930x.mk @@ -17,6 +17,7 @@ define Device/hasivo_s1100w-8xgt-se DEVICE_VENDOR := Hasivo DEVICE_MODEL := S1100W-8XGT-SE IMAGE_SIZE := 12288k + DEVICE_PACKAGES := rtl8264b-firmware $(Device/kernel-lzma) endef TARGET_DEVICES += hasivo_s1100w-8xgt-se @@ -126,7 +127,7 @@ define Device/xikestor_sks8300-8t UIMAGE_MAGIC := 0x93000000 DEVICE_VENDOR := XikeStor DEVICE_MODEL := SKS8300-8T - DEVICE_PACKAGES := kmod-hwmon-lm75 + DEVICE_PACKAGES := kmod-hwmon-lm75 rtl8261n-firmware IMAGE_SIZE := 20480k $(Device/kernel-lzma) IMAGE/sysupgrade.bin := \ @@ -160,6 +161,7 @@ define Device/xikestor_sks8300-12e2t2x UIMAGE_MAGIC := 0x93000000 DEVICE_VENDOR := XikeStor DEVICE_MODEL := SKS8300-12E2T2X + DEVICE_PACKAGES := rtl8261n-firmware IMAGE_SIZE := 20480k $(Device/kernel-lzma) IMAGE/sysupgrade.bin := \ @@ -250,6 +252,7 @@ TARGET_DEVICES += zyxel_xgs1250-12-a1 define Device/zyxel_xgs1250-12-b1 $(Device/zyxel_xgs1250-12-common) DEVICE_VARIANT := B1 + DEVICE_PACKAGES += rtl8261n-firmware endef TARGET_DEVICES += zyxel_xgs1250-12-b1 diff --git a/target/linux/realtek/patches-6.18/024-02-v7.1-net-phy-realtek-add-RTL8224-pair-order-support.patch b/target/linux/realtek/patches-6.18/024-02-v7.1-net-phy-realtek-add-RTL8224-pair-order-support.patch index 7de7fc98c10..6f3758696a7 100644 --- a/target/linux/realtek/patches-6.18/024-02-v7.1-net-phy-realtek-add-RTL8224-pair-order-support.patch +++ b/target/linux/realtek/patches-6.18/024-02-v7.1-net-phy-realtek-add-RTL8224-pair-order-support.patch @@ -27,17 +27,17 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/realtek/Kconfig +++ b/drivers/net/phy/realtek/Kconfig -@@ -1,6 +1,7 @@ - # SPDX-License-Identifier: GPL-2.0-only +@@ -2,6 +2,7 @@ config REALTEK_PHY tristate "Realtek PHYs" + select PHY_COMMON_PROPS + select PHY_PACKAGE help Currently supports RTL821x/RTL822x and fast ethernet PHYs --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -19,6 +19,7 @@ +@@ -21,6 +21,7 @@ #include #include @@ -45,7 +45,7 @@ Signed-off-by: Jakub Kicinski #include "realtek.h" #define RTL8201F_IER 0x13 -@@ -175,6 +176,8 @@ +@@ -177,6 +178,8 @@ #define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12) #define RTL8221B_PHYCR1_PHYAD_0_EN BIT(13) @@ -54,7 +54,7 @@ Signed-off-by: Jakub Kicinski #define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE_ON BIT(12) -@@ -1871,6 +1874,66 @@ static int rtl8224_cable_test_get_status +@@ -1889,6 +1892,66 @@ static int rtl8224_cable_test_get_status return rtl8224_cable_test_report(phydev, finished); } @@ -121,7 +121,7 @@ Signed-off-by: Jakub Kicinski static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) { int val; -@@ -2472,6 +2535,8 @@ static struct phy_driver realtek_drvs[] +@@ -3096,6 +3159,8 @@ static struct phy_driver realtek_drvs[] PHY_ID_MATCH_EXACT(0x001ccad0), .name = "RTL8224 2.5Gbps PHY", .flags = PHY_POLL_CABLE_TEST, diff --git a/target/linux/realtek/patches-6.18/024-04-v7.1-net-phy-realtek-add-RTL8224-polarity-support.patch b/target/linux/realtek/patches-6.18/024-04-v7.1-net-phy-realtek-add-RTL8224-polarity-support.patch index 101b35b0b9f..fc84b749812 100644 --- a/target/linux/realtek/patches-6.18/024-04-v7.1-net-phy-realtek-add-RTL8224-polarity-support.patch +++ b/target/linux/realtek/patches-6.18/024-04-v7.1-net-phy-realtek-add-RTL8224-polarity-support.patch @@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -177,6 +177,7 @@ +@@ -179,6 +179,7 @@ #define RTL8221B_PHYCR1_PHYAD_0_EN BIT(13) #define RTL8224_VND1_MDI_PAIR_SWAP 0xa90 @@ -32,7 +32,7 @@ Signed-off-by: Jakub Kicinski #define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE_ON BIT(12) -@@ -1922,9 +1923,40 @@ static int rtl8224_mdi_config_order(stru +@@ -1940,9 +1941,40 @@ static int rtl8224_mdi_config_order(stru order ? BIT(port_offset) : 0); } diff --git a/target/linux/realtek/patches-6.18/720-add-rtl-phy.patch b/target/linux/realtek/patches-6.18/720-add-rtl-phy.patch index 537a2bcecbb..925e7f96d13 100644 --- a/target/linux/realtek/patches-6.18/720-add-rtl-phy.patch +++ b/target/linux/realtek/patches-6.18/720-add-rtl-phy.patch @@ -9,14 +9,14 @@ configuration option for the phy module. Submitted-by: Birger Koblitz --- a/drivers/net/phy/realtek/Makefile +++ b/drivers/net/phy/realtek/Makefile -@@ -2,3 +2,4 @@ - realtek-y += realtek_main.o +@@ -4,3 +4,4 @@ realtek-y += phy_patch.o + realtek-y += phy_patch_rtl826x.o realtek-$(CONFIG_REALTEK_PHY_HWMON) += realtek_hwmon.o obj-$(CONFIG_REALTEK_PHY) += realtek.o +obj-$(CONFIG_REALTEK_PHY_MULTIPORT) += realtek_multiport.o --- a/drivers/net/phy/realtek/Kconfig +++ b/drivers/net/phy/realtek/Kconfig -@@ -5,6 +5,13 @@ config REALTEK_PHY +@@ -6,6 +6,13 @@ config REALTEK_PHY help Currently supports RTL821x/RTL822x and fast ethernet PHYs 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 index 398c19f1790..35d95874eea 100644 --- 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 @@ -30,7 +30,7 @@ Signed-off-by: Jan Hoffmann --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -1496,6 +1496,148 @@ static unsigned int rtl822x_inband_caps( +@@ -1514,6 +1514,148 @@ static unsigned int rtl822x_inband_caps( } } @@ -179,7 +179,7 @@ Signed-off-by: Jan Hoffmann static int rtl822xb_get_rate_matching(struct phy_device *phydev, phy_interface_t iface) { -@@ -2459,7 +2601,7 @@ static struct phy_driver realtek_drvs[] +@@ -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, diff --git a/target/linux/realtek/patches-6.18/743-net-realtek-serdes-configuration.patch b/target/linux/realtek/patches-6.18/743-net-realtek-serdes-configuration.patch index 2e820fbcc5c..d67d2eb2b4a 100644 --- a/target/linux/realtek/patches-6.18/743-net-realtek-serdes-configuration.patch +++ b/target/linux/realtek/patches-6.18/743-net-realtek-serdes-configuration.patch @@ -1,6 +1,6 @@ --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -171,6 +171,41 @@ +@@ -173,6 +173,41 @@ #define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4) @@ -42,7 +42,7 @@ #define RTL8221B_PHYCR1 0xa430 #define RTL8221B_PHYCR1_ALDPS_EN BIT(2) #define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12) -@@ -2040,6 +2075,147 @@ exit: +@@ -2058,6 +2093,147 @@ exit: return ret; } @@ -190,7 +190,7 @@ static int rtl8224_mdi_config_order(struct phy_device *phydev) { struct device_node *np = phydev->mdio.dev.of_node; -@@ -2094,6 +2270,10 @@ static int rtl8224_config_init(struct ph +@@ -2112,6 +2288,10 @@ static int rtl8224_config_init(struct ph { int ret; diff --git a/target/linux/realtek/rtl930x/config-6.18 b/target/linux/realtek/rtl930x/config-6.18 index eed9581691f..730b35e16f3 100644 --- a/target/linux/realtek/rtl930x/config-6.18 +++ b/target/linux/realtek/rtl930x/config-6.18 @@ -223,7 +223,6 @@ CONFIG_REGMAP_MMIO=y CONFIG_RESET_CONTROLLER=y CONFIG_RFS_ACCEL=y CONFIG_RPS=y -CONFIG_RTL8261N_PHY=y # CONFIG_RTL838X is not set # CONFIG_RTL839X is not set CONFIG_RTL930X=y diff --git a/target/linux/realtek/rtl930x_nand/config-6.18 b/target/linux/realtek/rtl930x_nand/config-6.18 index 765b500c23d..8f0e183d44b 100644 --- a/target/linux/realtek/rtl930x_nand/config-6.18 +++ b/target/linux/realtek/rtl930x_nand/config-6.18 @@ -233,7 +233,6 @@ CONFIG_REGMAP_MMIO=y CONFIG_RESET_CONTROLLER=y CONFIG_RFS_ACCEL=y CONFIG_RPS=y -CONFIG_RTL8261N_PHY=y # CONFIG_RTL838X is not set # CONFIG_RTL839X is not set CONFIG_RTL930X=y