--- /dev/null
+From fd8318a32573d73eb20637a0c80689de0dc98169 Mon Sep 17 00:00:00 2001
+Date: Fri, 3 Jan 2025 16:41:13 +0800
+Subject: [PATCH] PM: sleep: wakeirq: Introduce device-managed variant of
+ dev_pm_set_wake_irq()
+
+Add device-managed variant of dev_pm_set_wake_irq which automatically
+clear the wake irq on device destruction to simplify error handling
+and resource management in drivers.
+
+---
+ drivers/base/power/wakeirq.c | 26 ++++++++++++++++++++++++++
+ include/linux/pm_wakeirq.h | 6 ++++++
+ 2 files changed, 32 insertions(+)
+
+--- a/drivers/base/power/wakeirq.c
++++ b/drivers/base/power/wakeirq.c
+@@ -103,6 +103,32 @@ void dev_pm_clear_wake_irq(struct device
+ }
+ EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
++static void devm_pm_clear_wake_irq(void *dev)
++{
++ dev_pm_clear_wake_irq(dev);
++}
++
++/**
++ * devm_pm_set_wake_irq - device-managed variant of dev_pm_set_wake_irq
++ * @dev: Device entry
++ * @irq: Device IO interrupt
++ *
++ *
++ * Attach a device IO interrupt as a wake IRQ, same with dev_pm_set_wake_irq,
++ * but the device will be auto clear wake capability on driver detach.
++ */
++int devm_pm_set_wake_irq(struct device *dev, int irq)
++{
++ int ret;
++
++ ret = dev_pm_set_wake_irq(dev, irq);
++ if (ret)
++ return ret;
++
++ return devm_add_action_or_reset(dev, devm_pm_clear_wake_irq, dev);
++}
++EXPORT_SYMBOL_GPL(devm_pm_set_wake_irq);
++
+ /**
+ * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
+ * @irq: Device specific dedicated wake-up interrupt
+--- a/include/linux/pm_wakeirq.h
++++ b/include/linux/pm_wakeirq.h
+@@ -10,6 +10,7 @@ extern int dev_pm_set_wake_irq(struct de
+ extern int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq);
+ extern int dev_pm_set_dedicated_wake_irq_reverse(struct device *dev, int irq);
+ extern void dev_pm_clear_wake_irq(struct device *dev);
++extern int devm_pm_set_wake_irq(struct device *dev, int irq);
+
+ #else /* !CONFIG_PM */
+
+@@ -32,5 +33,10 @@ static inline void dev_pm_clear_wake_irq
+ {
+ }
+
++static inline int devm_pm_set_wake_irq(struct device *dev, int irq)
++{
++ return 0;
++}
++
+ #endif /* CONFIG_PM */
+ #endif /* _LINUX_PM_WAKEIRQ_H */
--- /dev/null
+From 2c67301584f2671e320236df6bbe75ae09feb4d0 Mon Sep 17 00:00:00 2001
+Date: Sat, 11 Oct 2025 13:02:49 +0200
+Subject: [PATCH] net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not present
+
+The driver is currently checking for PHYCR2 register presence in
+rtl8211f_config_init(), but it does so after accessing PHYCR2 to
+disable EEE. This was introduced in commit bfc17c165835 ("net:
+phy: realtek: disable PHY-mode EEE"). Move the PHYCR2 presence
+test before the EEE disablement and simplify the code.
+
+Fixes: bfc17c165835 ("net: phy: realtek: disable PHY-mode EEE")
+---
+ drivers/net/phy/realtek/realtek_main.c | 23 +++++++++++------------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -589,26 +589,25 @@ static int rtl8211f_config_init(struct p
+ str_enabled_disabled(val_rxdly));
+ }
+
++ if (!priv->has_phycr2)
++ return 0;
++
+ /* Disable PHY-mode EEE so LPI is passed to the MAC */
+ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2,
+ RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0);
+ if (ret)
+ return ret;
+
+- if (priv->has_phycr2) {
+- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
+- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN,
+- priv->phycr2);
+- if (ret < 0) {
+- dev_err(dev, "clkout configuration failed: %pe\n",
+- ERR_PTR(ret));
+- return ret;
+- }
+-
+- return genphy_soft_reset(phydev);
++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
++ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN,
++ priv->phycr2);
++ if (ret < 0) {
++ dev_err(dev, "clkout configuration failed: %pe\n",
++ ERR_PTR(ret));
++ return ret;
+ }
+
+- return 0;
++ return genphy_soft_reset(phydev);
+ }
+
+ static int rtl821x_suspend(struct phy_device *phydev)
--- /dev/null
+From b826bf795564ddef6402cf2cb522ae035bd117ae Mon Sep 17 00:00:00 2001
+Date: Wed, 13 Aug 2025 11:04:45 +0100
+Subject: [PATCH] net: phy: realtek: fix RTL8211F wake-on-lan support
+
+Implement Wake-on-Lan for RTL8211F correctly. The existing
+implementation has multiple issues:
+
+1. It assumes that Wake-on-Lan can always be used, whether or not the
+ interrupt is wired, and whether or not the interrupt is capable of
+ waking the system. This breaks the ability for MAC drivers to detect
+ whether the PHY WoL is functional.
+2. switching the interrupt pin in the .set_wol() method to PMEB mode
+ immediately silences link-state interrupts, which breaks phylib
+ when interrupts are being used rather than polling mode.
+3. the code claiming to "reset WOL status" was doing nothing of the
+ sort. Bit 15 in page 0xd8a register 17 controls WoL reset, and
+ needs to be pulsed low to reset the WoL state. This bit was always
+ written as '1', resulting in no reset.
+4. not resetting WoL state results in the PMEB pin remaining asserted,
+ which in turn leads to an interrupt storm. Only resetting the WoL
+ state in .set_wol() is not sufficient.
+5. PMEB mode does not allow software detection of the wake-up event as
+ there is no status bit to indicate we received the WoL packet.
+6. across reboots of at least the Jetson Xavier NX system, the WoL
+ configuration is preserved.
+
+Fix all of these issues by essentially rewriting the support. We:
+1. clear the WoL event enable register at probe time.
+2. detect whether we can support wake-up by having a valid interrupt,
+ and the "wakeup-source" property in DT. If we can, then we mark
+ the MDIO device as wakeup capable, and associate the interrupt
+ with the wakeup source.
+3. arrange for the get_wol() and set_wol() implementations to handle
+ the case where the MDIO device has not been marked as wakeup
+ capable (thereby returning no WoL support, and refusing to enable
+ WoL support.)
+4. avoid switching to PMEB mode, instead using INTB mode with the
+ interrupt enable, reconfiguring the interrupt enables at suspend
+ time, and restoring their original state at resume time (we track
+ the state of the interrupt enable register in .config_intr()
+ register.)
+5. move WoL reset from .set_wol() to the suspend function to ensure
+ that WoL state is cleared prior to suspend. This is necessary
+ after the PME interrupt has been enabled as a second WoL packet
+ will not re-raise a previously cleared PME interrupt.
+6. when a PME interrupt (for wakeup) is asserted, pass this to the
+ PM wakeup so it knows which device woke the system.
+
+This fixes WoL support in the Realtek RTL8211F driver when used on the
+nVidia Jetson Xavier NX platform, and needs to be applied before stmmac
+patches which allow these platforms to forward the ethtool WoL commands
+to the Realtek PHY.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 172 ++++++++++++++++++++-----
+ 1 file changed, 140 insertions(+), 32 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -10,6 +10,7 @@
+ #include <linux/bitops.h>
+ #include <linux/of.h>
+ #include <linux/phy.h>
++#include <linux/pm_wakeirq.h>
+ #include <linux/netdevice.h>
+ #include <linux/module.h>
+ #include <linux/delay.h>
+@@ -31,6 +32,7 @@
+ #define RTL821x_INER 0x12
+ #define RTL8211B_INER_INIT 0x6400
+ #define RTL8211E_INER_LINK_STATUS BIT(10)
++#define RTL8211F_INER_PME BIT(7)
+ #define RTL8211F_INER_LINK_STATUS BIT(4)
+
+ #define RTL821x_INSR 0x13
+@@ -96,17 +98,13 @@
+ #define RTL8211F_RXCR 0x15
+ #define RTL8211F_RX_DELAY BIT(3)
+
+-/* RTL8211F WOL interrupt configuration */
+-#define RTL8211F_INTBCR_PAGE 0xd40
+-#define RTL8211F_INTBCR 0x16
+-#define RTL8211F_INTBCR_INTB_PMEB BIT(5)
+-
+ /* RTL8211F WOL settings */
+-#define RTL8211F_WOL_SETTINGS_PAGE 0xd8a
++#define RTL8211F_WOL_PAGE 0xd8a
+ #define RTL8211F_WOL_SETTINGS_EVENTS 16
+ #define RTL8211F_WOL_EVENT_MAGIC BIT(12)
+-#define RTL8211F_WOL_SETTINGS_STATUS 17
+-#define RTL8211F_WOL_STATUS_RESET (BIT(15) | 0x1fff)
++#define RTL8211F_WOL_RST_RMSQ 17
++#define RTL8211F_WOL_RG_RSTB BIT(15)
++#define RTL8211F_WOL_RMSQ 0x1fff
+
+ /* RTL8211F Unique phyiscal and multicast address (WOL) */
+ #define RTL8211F_PHYSICAL_ADDR_PAGE 0xd8c
+@@ -172,7 +170,8 @@ struct rtl821x_priv {
+ u16 phycr2;
+ bool has_phycr2;
+ struct clk *clk;
+- u32 saved_wolopts;
++ /* rtl8211f */
++ u16 iner;
+ };
+
+ static int rtl821x_read_page(struct phy_device *phydev)
+@@ -255,6 +254,34 @@ static int rtl821x_probe(struct phy_devi
+ return 0;
+ }
+
++static int rtl8211f_probe(struct phy_device *phydev)
++{
++ struct device *dev = &phydev->mdio.dev;
++ int ret;
++
++ ret = rtl821x_probe(phydev);
++ if (ret < 0)
++ return ret;
++
++ /* Disable all PME events */
++ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE,
++ RTL8211F_WOL_SETTINGS_EVENTS, 0);
++ if (ret < 0)
++ return ret;
++
++ /* Mark this PHY as wakeup capable and register the interrupt as a
++ * wakeup IRQ if the PHY is marked as a wakeup source in firmware,
++ * and the interrupt is valid.
++ */
++ if (device_property_read_bool(dev, "wakeup-source") &&
++ phy_interrupt_is_valid(phydev)) {
++ device_set_wakeup_capable(dev, true);
++ devm_pm_set_wake_irq(dev, phydev->irq);
++ }
++
++ return ret;
++}
++
+ static int rtl8201_ack_interrupt(struct phy_device *phydev)
+ {
+ int err;
+@@ -352,6 +379,7 @@ static int rtl8211e_config_intr(struct p
+
+ static int rtl8211f_config_intr(struct phy_device *phydev)
+ {
++ struct rtl821x_priv *priv = phydev->priv;
+ u16 val;
+ int err;
+
+@@ -362,8 +390,10 @@ static int rtl8211f_config_intr(struct p
+
+ val = RTL8211F_INER_LINK_STATUS;
+ err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
++ if (err == 0)
++ priv->iner = val;
+ } else {
+- val = 0;
++ priv->iner = val = 0;
+ err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
+ if (err)
+ return err;
+@@ -426,21 +456,34 @@ static irqreturn_t rtl8211f_handle_inter
+ return IRQ_NONE;
+ }
+
+- if (!(irq_status & RTL8211F_INER_LINK_STATUS))
+- return IRQ_NONE;
++ if (irq_status & RTL8211F_INER_LINK_STATUS) {
++ phy_trigger_machine(phydev);
++ return IRQ_HANDLED;
++ }
+
+- phy_trigger_machine(phydev);
++ if (irq_status & RTL8211F_INER_PME) {
++ pm_wakeup_event(&phydev->mdio.dev, 0);
++ return IRQ_HANDLED;
++ }
+
+- return IRQ_HANDLED;
++ return IRQ_NONE;
+ }
+
+ static void rtl8211f_get_wol(struct phy_device *dev, struct ethtool_wolinfo *wol)
+ {
+ int wol_events;
+
++ /* If the PHY is not capable of waking the system, then WoL can not
++ * be supported.
++ */
++ if (!device_can_wakeup(&dev->mdio.dev)) {
++ wol->supported = 0;
++ return;
++ }
++
+ wol->supported = WAKE_MAGIC;
+
+- wol_events = phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS);
++ wol_events = phy_read_paged(dev, RTL8211F_WOL_PAGE, RTL8211F_WOL_SETTINGS_EVENTS);
+ if (wol_events < 0)
+ return;
+
+@@ -453,6 +496,9 @@ static int rtl8211f_set_wol(struct phy_d
+ const u8 *mac_addr = dev->attached_dev->dev_addr;
+ int oldpage;
+
++ if (!device_can_wakeup(&dev->mdio.dev))
++ return -EOPNOTSUPP;
++
+ oldpage = phy_save_page(dev);
+ if (oldpage < 0)
+ goto err;
+@@ -464,25 +510,23 @@ static int rtl8211f_set_wol(struct phy_d
+ __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD1, mac_addr[3] << 8 | (mac_addr[2]));
+ __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD2, mac_addr[5] << 8 | (mac_addr[4]));
+
+- /* Enable magic packet matching and reset WOL status */
+- rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE);
++ /* Enable magic packet matching */
++ rtl821x_write_page(dev, RTL8211F_WOL_PAGE);
+ __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, RTL8211F_WOL_EVENT_MAGIC);
+- __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET);
+-
+- /* Enable the WOL interrupt */
+- rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE);
+- __phy_set_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB);
++ /* Set the maximum packet size, and assert WoL reset */
++ __phy_write(dev, RTL8211F_WOL_RST_RMSQ, RTL8211F_WOL_RMSQ);
+ } else {
+- /* Disable the WOL interrupt */
+- rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE);
+- __phy_clear_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB);
+-
+- /* Disable magic packet matching and reset WOL status */
+- rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE);
++ /* Disable magic packet matching */
++ rtl821x_write_page(dev, RTL8211F_WOL_PAGE);
+ __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, 0);
+- __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET);
++
++ /* Place WoL in reset */
++ __phy_clear_bits(dev, RTL8211F_WOL_RST_RMSQ,
++ RTL8211F_WOL_RG_RSTB);
+ }
+
++ device_set_wakeup_enable(&dev->mdio.dev, !!(wol->wolopts & WAKE_MAGIC));
++
+ err:
+ return phy_restore_page(dev, oldpage, 0);
+ }
+@@ -627,6 +671,52 @@ static int rtl821x_suspend(struct phy_de
+ return ret;
+ }
+
++static int rtl8211f_suspend(struct phy_device *phydev)
++{
++ u16 wol_rst;
++ int ret;
++
++ ret = rtl821x_suspend(phydev);
++ if (ret < 0)
++ return ret;
++
++ /* If a PME event is enabled, then configure the interrupt for
++ * PME events only, disabling link interrupt. We avoid switching
++ * to PMEB mode as we don't have a status bit for that.
++ */
++ if (device_may_wakeup(&phydev->mdio.dev)) {
++ ret = phy_write_paged(phydev, 0xa42, RTL821x_INER,
++ RTL8211F_INER_PME);
++ if (ret < 0)
++ goto err;
++
++ /* Read the INSR to clear any pending interrupt */
++ phy_read_paged(phydev, RTL8211F_INSR_PAGE, RTL8211F_INSR);
++
++ /* Reset the WoL to ensure that an event is picked up.
++ * Unless we do this, even if we receive another packet,
++ * we may not have a PME interrupt raised.
++ */
++ ret = phy_read_paged(phydev, RTL8211F_WOL_PAGE,
++ RTL8211F_WOL_RST_RMSQ);
++ if (ret < 0)
++ goto err;
++
++ wol_rst = ret & ~RTL8211F_WOL_RG_RSTB;
++ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE,
++ RTL8211F_WOL_RST_RMSQ, wol_rst);
++ if (ret < 0)
++ goto err;
++
++ wol_rst |= RTL8211F_WOL_RG_RSTB;
++ ret = phy_write_paged(phydev, RTL8211F_WOL_PAGE,
++ RTL8211F_WOL_RST_RMSQ, wol_rst);
++ }
++
++err:
++ return ret;
++}
++
+ static int rtl821x_resume(struct phy_device *phydev)
+ {
+ struct rtl821x_priv *priv = phydev->priv;
+@@ -644,6 +734,24 @@ static int rtl821x_resume(struct phy_dev
+ return 0;
+ }
+
++static int rtl8211f_resume(struct phy_device *phydev)
++{
++ struct rtl821x_priv *priv = phydev->priv;
++ int ret;
++
++ ret = rtl821x_resume(phydev);
++ if (ret < 0)
++ return ret;
++
++ /* If the device was programmed for a PME event, restore the interrupt
++ * enable so phylib can receive link state interrupts.
++ */
++ if (device_may_wakeup(&phydev->mdio.dev))
++ ret = phy_write_paged(phydev, 0xa42, RTL821x_INER, priv->iner);
++
++ return ret;
++}
++
+ static int rtl8211x_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+ {
+@@ -1639,15 +1747,15 @@ static struct phy_driver realtek_drvs[]
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc916),
+ .name = "RTL8211F Gigabit Ethernet",
+- .probe = rtl821x_probe,
++ .probe = rtl8211f_probe,
+ .config_init = &rtl8211f_config_init,
+ .read_status = rtlgen_read_status,
+ .config_intr = &rtl8211f_config_intr,
+ .handle_interrupt = rtl8211f_handle_interrupt,
+ .set_wol = rtl8211f_set_wol,
+ .get_wol = rtl8211f_get_wol,
+- .suspend = rtl821x_suspend,
+- .resume = rtl821x_resume,
++ .suspend = rtl8211f_suspend,
++ .resume = rtl8211f_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ .flags = PHY_ALWAYS_CALL_SUSPEND,
+++ /dev/null
-From f63f21e82ecafd288b100ea161247820bf1e92c4 Mon Sep 17 00:00:00 2001
-Date: Mon, 25 Aug 2025 23:09:49 +0200
-Subject: [PATCH] net: phy: realtek: support for TRIGGER_NETDEV_LINK on
- RTL8211E and RTL8211F
-
-This patch adds support for the TRIGGER_NETDEV_LINK trigger. It activates
-the LED when a link is established, regardless of the speed.
-
-Tested on Orange Pi PC2 with RTL8211E PHY.
-
----
- drivers/net/phy/realtek/realtek_main.c | 39 +++++++++++++++++++++-----
- 1 file changed, 32 insertions(+), 7 deletions(-)
-
---- a/drivers/net/phy/realtek/realtek_main.c
-+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -648,7 +648,8 @@ static int rtl821x_resume(struct phy_dev
- static int rtl8211x_led_hw_is_supported(struct phy_device *phydev, u8 index,
- unsigned long rules)
- {
-- const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) |
-+ const unsigned long mask = BIT(TRIGGER_NETDEV_LINK) |
-+ BIT(TRIGGER_NETDEV_LINK_10) |
- BIT(TRIGGER_NETDEV_LINK_100) |
- BIT(TRIGGER_NETDEV_LINK_1000) |
- BIT(TRIGGER_NETDEV_RX) |
-@@ -706,6 +707,12 @@ static int rtl8211f_led_hw_control_get(s
- if (val & RTL8211F_LEDCR_LINK_1000)
- __set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-
-+ if ((val & RTL8211F_LEDCR_LINK_10) &&
-+ (val & RTL8211F_LEDCR_LINK_100) &&
-+ (val & RTL8211F_LEDCR_LINK_1000)) {
-+ __set_bit(TRIGGER_NETDEV_LINK, rules);
-+ }
-+
- if (val & RTL8211F_LEDCR_ACT_TXRX) {
- __set_bit(TRIGGER_NETDEV_RX, rules);
- __set_bit(TRIGGER_NETDEV_TX, rules);
-@@ -723,14 +730,20 @@ static int rtl8211f_led_hw_control_set(s
- if (index >= RTL8211x_LED_COUNT)
- return -EINVAL;
-
-- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
-+ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) {
- reg |= RTL8211F_LEDCR_LINK_10;
-+ }
-
-- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
-+ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) {
- reg |= RTL8211F_LEDCR_LINK_100;
-+ }
-
-- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
-+ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) {
- reg |= RTL8211F_LEDCR_LINK_1000;
-+ }
-
- if (test_bit(TRIGGER_NETDEV_RX, &rules) ||
- test_bit(TRIGGER_NETDEV_TX, &rules)) {
-@@ -778,6 +791,12 @@ static int rtl8211e_led_hw_control_get(s
- if (cr2 & RTL8211E_LEDCR2_LINK_1000)
- __set_bit(TRIGGER_NETDEV_LINK_1000, rules);
-
-+ if ((cr2 & RTL8211E_LEDCR2_LINK_10) &&
-+ (cr2 & RTL8211E_LEDCR2_LINK_100) &&
-+ (cr2 & RTL8211E_LEDCR2_LINK_1000)) {
-+ __set_bit(TRIGGER_NETDEV_LINK, rules);
-+ }
-+
- return ret;
- }
-
-@@ -805,14 +824,20 @@ static int rtl8211e_led_hw_control_set(s
- if (ret < 0)
- return ret;
-
-- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
-+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
-+ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) {
- cr2 |= RTL8211E_LEDCR2_LINK_10;
-+ }
-
-- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
-+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
-+ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) {
- cr2 |= RTL8211E_LEDCR2_LINK_100;
-+ }
-
-- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
-+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
-+ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) {
- cr2 |= RTL8211E_LEDCR2_LINK_1000;
-+ }
-
- cr2 <<= RTL8211E_LEDCR2_SHIFT * index;
- ret = rtl821x_modify_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE,
+++ /dev/null
-From 2c67301584f2671e320236df6bbe75ae09feb4d0 Mon Sep 17 00:00:00 2001
-Date: Sat, 11 Oct 2025 13:02:49 +0200
-Subject: [PATCH] net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not present
-
-The driver is currently checking for PHYCR2 register presence in
-rtl8211f_config_init(), but it does so after accessing PHYCR2 to
-disable EEE. This was introduced in commit bfc17c165835 ("net:
-phy: realtek: disable PHY-mode EEE"). Move the PHYCR2 presence
-test before the EEE disablement and simplify the code.
-
-Fixes: bfc17c165835 ("net: phy: realtek: disable PHY-mode EEE")
----
- drivers/net/phy/realtek/realtek_main.c | 23 +++++++++++------------
- 1 file changed, 11 insertions(+), 12 deletions(-)
-
---- a/drivers/net/phy/realtek/realtek_main.c
-+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -589,26 +589,25 @@ static int rtl8211f_config_init(struct p
- str_enabled_disabled(val_rxdly));
- }
-
-+ if (!priv->has_phycr2)
-+ return 0;
-+
- /* Disable PHY-mode EEE so LPI is passed to the MAC */
- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2,
- RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0);
- if (ret)
- return ret;
-
-- if (priv->has_phycr2) {
-- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
-- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN,
-- priv->phycr2);
-- if (ret < 0) {
-- dev_err(dev, "clkout configuration failed: %pe\n",
-- ERR_PTR(ret));
-- return ret;
-- }
--
-- return genphy_soft_reset(phydev);
-+ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
-+ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN,
-+ priv->phycr2);
-+ if (ret < 0) {
-+ dev_err(dev, "clkout configuration failed: %pe\n",
-+ ERR_PTR(ret));
-+ return ret;
- }
-
-- return 0;
-+ return genphy_soft_reset(phydev);
- }
-
- static int rtl821x_suspend(struct phy_device *phydev)
--- /dev/null
+From ffff5c8fc2af2218a3332b3d5b97654599d50cde Mon Sep 17 00:00:00 2001
+Date: Thu, 16 Oct 2025 21:22:52 +0200
+Subject: [PATCH] net: phy: realtek: fix rtl8221b-vm-cg name
+
+When splitting the RTL8221B-VM-CG into C22 and C45 variants, the name was
+accidentally changed to RTL8221B-VN-CG. This patch brings back the previous
+part number.
+
+Fixes: ad5ce743a6b0 ("net: phy: realtek: Add driver instances for rtl8221b via Clause 45")
+---
+ drivers/net/phy/realtek/realtek_main.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -154,7 +154,7 @@
+ #define RTL_8211FVD_PHYID 0x001cc878
+ #define RTL_8221B 0x001cc840
+ #define RTL_8221B_VB_CG 0x001cc849
+-#define RTL_8221B_VN_CG 0x001cc84a
++#define RTL_8221B_VM_CG 0x001cc84a
+ #define RTL_8251B 0x001cc862
+ #define RTL_8261C 0x001cc890
+
+@@ -1498,16 +1498,16 @@ static int rtl8221b_vb_cg_c45_match_phy_
+ return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
+ }
+
+-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
++static int rtl8221b_vm_cg_c22_match_phy_device(struct phy_device *phydev,
+ const struct phy_driver *phydrv)
+ {
+- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
++ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, false);
+ }
+
+-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
++static int rtl8221b_vm_cg_c45_match_phy_device(struct phy_device *phydev,
+ const struct phy_driver *phydrv)
+ {
+- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
++ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, true);
+ }
+
+ static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
+@@ -1854,7 +1854,7 @@ static struct phy_driver realtek_drvs[]
+ .suspend = genphy_c45_pma_suspend,
+ .resume = rtlgen_c45_resume,
+ }, {
+- .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
++ .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device,
+ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
+ .probe = rtl822x_probe,
+ .get_features = rtl822x_get_features,
+@@ -1867,8 +1867,8 @@ static struct phy_driver realtek_drvs[]
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ }, {
+- .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
+- .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
++ .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device,
++ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)",
+ .probe = rtl822x_probe,
+ .config_init = rtl822xb_config_init,
+ .get_rate_matching = rtl822xb_get_rate_matching,
--- /dev/null
+From f63f21e82ecafd288b100ea161247820bf1e92c4 Mon Sep 17 00:00:00 2001
+Date: Mon, 25 Aug 2025 23:09:49 +0200
+Subject: [PATCH] net: phy: realtek: support for TRIGGER_NETDEV_LINK on
+ RTL8211E and RTL8211F
+
+This patch adds support for the TRIGGER_NETDEV_LINK trigger. It activates
+the LED when a link is established, regardless of the speed.
+
+Tested on Orange Pi PC2 with RTL8211E PHY.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 39 +++++++++++++++++++++-----
+ 1 file changed, 32 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -755,7 +755,8 @@ static int rtl8211f_resume(struct phy_de
+ static int rtl8211x_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+ {
+- const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) |
++ const unsigned long mask = BIT(TRIGGER_NETDEV_LINK) |
++ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_RX) |
+@@ -813,6 +814,12 @@ static int rtl8211f_led_hw_control_get(s
+ if (val & RTL8211F_LEDCR_LINK_1000)
+ __set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+
++ if ((val & RTL8211F_LEDCR_LINK_10) &&
++ (val & RTL8211F_LEDCR_LINK_100) &&
++ (val & RTL8211F_LEDCR_LINK_1000)) {
++ __set_bit(TRIGGER_NETDEV_LINK, rules);
++ }
++
+ if (val & RTL8211F_LEDCR_ACT_TXRX) {
+ __set_bit(TRIGGER_NETDEV_RX, rules);
+ __set_bit(TRIGGER_NETDEV_TX, rules);
+@@ -830,14 +837,20 @@ static int rtl8211f_led_hw_control_set(s
+ if (index >= RTL8211x_LED_COUNT)
+ return -EINVAL;
+
+- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
++ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) {
+ reg |= RTL8211F_LEDCR_LINK_10;
++ }
+
+- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
++ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) {
+ reg |= RTL8211F_LEDCR_LINK_100;
++ }
+
+- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
++ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) {
+ reg |= RTL8211F_LEDCR_LINK_1000;
++ }
+
+ if (test_bit(TRIGGER_NETDEV_RX, &rules) ||
+ test_bit(TRIGGER_NETDEV_TX, &rules)) {
+@@ -885,6 +898,12 @@ static int rtl8211e_led_hw_control_get(s
+ if (cr2 & RTL8211E_LEDCR2_LINK_1000)
+ __set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+
++ if ((cr2 & RTL8211E_LEDCR2_LINK_10) &&
++ (cr2 & RTL8211E_LEDCR2_LINK_100) &&
++ (cr2 & RTL8211E_LEDCR2_LINK_1000)) {
++ __set_bit(TRIGGER_NETDEV_LINK, rules);
++ }
++
+ return ret;
+ }
+
+@@ -912,14 +931,20 @@ static int rtl8211e_led_hw_control_set(s
+ if (ret < 0)
+ return ret;
+
+- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
++ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) {
+ cr2 |= RTL8211E_LEDCR2_LINK_10;
++ }
+
+- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
++ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) {
+ cr2 |= RTL8211E_LEDCR2_LINK_100;
++ }
+
+- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
++ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) {
+ cr2 |= RTL8211E_LEDCR2_LINK_1000;
++ }
+
+ cr2 <<= RTL8211E_LEDCR2_SHIFT * index;
+ ret = rtl821x_modify_ext_page(phydev, RTL8211E_LEDCR_EXT_PAGE,
+++ /dev/null
-From ffff5c8fc2af2218a3332b3d5b97654599d50cde Mon Sep 17 00:00:00 2001
-Date: Thu, 16 Oct 2025 21:22:52 +0200
-Subject: [PATCH] net: phy: realtek: fix rtl8221b-vm-cg name
-
-When splitting the RTL8221B-VM-CG into C22 and C45 variants, the name was
-accidentally changed to RTL8221B-VN-CG. This patch brings back the previous
-part number.
-
-Fixes: ad5ce743a6b0 ("net: phy: realtek: Add driver instances for rtl8221b via Clause 45")
----
- drivers/net/phy/realtek/realtek_main.c | 16 ++++++++--------
- 1 file changed, 8 insertions(+), 8 deletions(-)
-
---- a/drivers/net/phy/realtek/realtek_main.c
-+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -156,7 +156,7 @@
- #define RTL_8211FVD_PHYID 0x001cc878
- #define RTL_8221B 0x001cc840
- #define RTL_8221B_VB_CG 0x001cc849
--#define RTL_8221B_VN_CG 0x001cc84a
-+#define RTL_8221B_VM_CG 0x001cc84a
- #define RTL_8251B 0x001cc862
- #define RTL_8261C 0x001cc890
-
-@@ -1415,16 +1415,16 @@ static int rtl8221b_vb_cg_c45_match_phy_
- return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
- }
-
--static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
-+static int rtl8221b_vm_cg_c22_match_phy_device(struct phy_device *phydev,
- const struct phy_driver *phydrv)
- {
-- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
-+ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, false);
- }
-
--static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
-+static int rtl8221b_vm_cg_c45_match_phy_device(struct phy_device *phydev,
- const struct phy_driver *phydrv)
- {
-- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
-+ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, true);
- }
-
- static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
-@@ -1771,7 +1771,7 @@ static struct phy_driver realtek_drvs[]
- .suspend = genphy_c45_pma_suspend,
- .resume = rtlgen_c45_resume,
- }, {
-- .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
-+ .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device,
- .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
- .probe = rtl822x_probe,
- .get_features = rtl822x_get_features,
-@@ -1784,8 +1784,8 @@ static struct phy_driver realtek_drvs[]
- .read_page = rtl821x_read_page,
- .write_page = rtl821x_write_page,
- }, {
-- .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
-- .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
-+ .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device,
-+ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)",
- .probe = rtl822x_probe,
- .config_init = rtl822xb_config_init,
- .get_rate_matching = rtl822xb_get_rate_matching,
+++ /dev/null
-From 61958b33ef0bab1c1874c933cd3910f495526782 Mon Sep 17 00:00:00 2001
-Date: Fri, 24 Oct 2025 11:49:00 +0200
-Subject: [PATCH] net: phy: realtek: Add RTL8224 cable testing support
-
-The RTL8224 can detect open pairs and short types (in same pair or some
-other pair). The distance to this problem can be estimated. This is done
-for each of the 4 pairs separately.
-
-It is not meant to be run while there is an active link partner because
-this interferes with the active test pulses.
-
-Output with open 50 m cable:
-
- Pair A code Open Circuit, source: TDR
- Pair A, fault length: 51.79m, source: TDR
- Pair B code Open Circuit, source: TDR
- Pair B, fault length: 51.28m, source: TDR
- Pair C code Open Circuit, source: TDR
- Pair C, fault length: 50.46m, source: TDR
- Pair D code Open Circuit, source: TDR
- Pair D, fault length: 51.12m, source: TDR
-
-Terminated cable:
-
- Pair A code OK, source: TDR
- Pair B code OK, source: TDR
- Pair C code OK, source: TDR
- Pair D code OK, source: TDR
-
-Shorted cable (both short types are at roughly the same distance)
-
- Pair A code Short to another pair, source: TDR
- Pair A, fault length: 2.35m, source: TDR
- Pair B code Short to another pair, source: TDR
- Pair B, fault length: 2.15m, source: TDR
- Pair C code OK, source: TDR
- Pair D code Short within Pair, source: TDR
- Pair D, fault length: 1.94m, source: TDR
-
----
- drivers/net/phy/realtek/realtek_main.c | 187 +++++++++++++++++++++++++
- 1 file changed, 187 insertions(+)
-
---- a/drivers/net/phy/realtek/realtek_main.c
-+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -8,6 +8,7 @@
- * Copyright (c) 2004 Freescale Semiconductor, Inc.
- */
- #include <linux/bitops.h>
-+#include <linux/ethtool_netlink.h>
- #include <linux/of.h>
- #include <linux/phy.h>
- #include <linux/netdevice.h>
-@@ -129,6 +130,27 @@
- */
- #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg))
-
-+#define RTL8224_MII_RTCT 0x11
-+#define RTL8224_MII_RTCT_ENABLE BIT(0)
-+#define RTL8224_MII_RTCT_PAIR_A BIT(4)
-+#define RTL8224_MII_RTCT_PAIR_B BIT(5)
-+#define RTL8224_MII_RTCT_PAIR_C BIT(6)
-+#define RTL8224_MII_RTCT_PAIR_D BIT(7)
-+#define RTL8224_MII_RTCT_DONE BIT(15)
-+
-+#define RTL8224_MII_SRAM_ADDR 0x1b
-+#define RTL8224_MII_SRAM_DATA 0x1c
-+
-+#define RTL8224_SRAM_RTCT_FAULT(pair) (0x8026 + (pair) * 4)
-+#define RTL8224_SRAM_RTCT_FAULT_BUSY BIT(0)
-+#define RTL8224_SRAM_RTCT_FAULT_OPEN BIT(3)
-+#define RTL8224_SRAM_RTCT_FAULT_SAME_SHORT BIT(4)
-+#define RTL8224_SRAM_RTCT_FAULT_OK BIT(5)
-+#define RTL8224_SRAM_RTCT_FAULT_DONE BIT(6)
-+#define RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT BIT(7)
-+
-+#define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4)
-+
- #define RTL8366RB_POWER_SAVE 0x15
- #define RTL8366RB_POWER_SAVE_ON BIT(12)
-
-@@ -1345,6 +1367,168 @@ static int rtl822xb_c45_read_status(stru
- return 0;
- }
-
-+static int rtl8224_cable_test_start(struct phy_device *phydev)
-+{
-+ u32 val;
-+ int ret;
-+
-+ /* disable auto-negotiation and force 1000/Full */
-+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2,
-+ RTL822X_VND2_C22_REG(MII_BMCR),
-+ BMCR_ANENABLE | BMCR_SPEED100 | BMCR_SPEED10,
-+ BMCR_SPEED1000 | BMCR_FULLDPLX);
-+ if (ret)
-+ return ret;
-+
-+ mdelay(500);
-+
-+ /* trigger cable test */
-+ val = RTL8224_MII_RTCT_ENABLE;
-+ val |= RTL8224_MII_RTCT_PAIR_A;
-+ val |= RTL8224_MII_RTCT_PAIR_B;
-+ val |= RTL8224_MII_RTCT_PAIR_C;
-+ val |= RTL8224_MII_RTCT_PAIR_D;
-+
-+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
-+ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT),
-+ RTL8224_MII_RTCT_DONE, val);
-+}
-+
-+static int rtl8224_sram_read(struct phy_device *phydev, u32 reg)
-+{
-+ int ret;
-+
-+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
-+ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_ADDR),
-+ reg);
-+ if (ret)
-+ return ret;
-+
-+ return phy_read_mmd(phydev, MDIO_MMD_VEND2,
-+ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_DATA));
-+}
-+
-+static int rtl8224_pair_len_get(struct phy_device *phydev, u32 pair)
-+{
-+ int cable_len;
-+ u32 reg_len;
-+ int ret;
-+ u32 cm;
-+
-+ reg_len = RTL8224_SRAM_RTCT_LEN(pair);
-+
-+ ret = rtl8224_sram_read(phydev, reg_len);
-+ if (ret < 0)
-+ return ret;
-+
-+ cable_len = ret & 0xff00;
-+
-+ ret = rtl8224_sram_read(phydev, reg_len + 1);
-+ if (ret < 0)
-+ return ret;
-+
-+ cable_len |= (ret & 0xff00) >> 8;
-+
-+ cable_len -= 620;
-+ cable_len = max(cable_len, 0);
-+
-+ cm = cable_len * 100 / 78;
-+
-+ return cm;
-+}
-+
-+static int rtl8224_cable_test_result_trans(u32 result)
-+{
-+ if (!(result & RTL8224_SRAM_RTCT_FAULT_DONE))
-+ return -EBUSY;
-+
-+ if (result & RTL8224_SRAM_RTCT_FAULT_OK)
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
-+
-+ if (result & RTL8224_SRAM_RTCT_FAULT_OPEN)
-+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
-+
-+ if (result & RTL8224_SRAM_RTCT_FAULT_SAME_SHORT)
-+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
-+
-+ if (result & RTL8224_SRAM_RTCT_FAULT_BUSY)
-+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-+
-+ if (result & RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT)
-+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
-+
-+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
-+}
-+
-+static int rtl8224_cable_test_report_pair(struct phy_device *phydev, unsigned int pair)
-+{
-+ int fault_rslt;
-+ int ret;
-+
-+ ret = rtl8224_sram_read(phydev, RTL8224_SRAM_RTCT_FAULT(pair));
-+ if (ret < 0)
-+ return ret;
-+
-+ fault_rslt = rtl8224_cable_test_result_trans(ret);
-+ if (fault_rslt < 0)
-+ return 0;
-+
-+ ret = ethnl_cable_test_result(phydev, pair, fault_rslt);
-+ if (ret < 0)
-+ return ret;
-+
-+ switch (fault_rslt) {
-+ case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
-+ case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
-+ case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
-+ ret = rtl8224_pair_len_get(phydev, pair);
-+ if (ret < 0)
-+ return ret;
-+
-+ return ethnl_cable_test_fault_length(phydev, pair, ret);
-+ default:
-+ return 0;
-+ }
-+}
-+
-+static int rtl8224_cable_test_report(struct phy_device *phydev, bool *finished)
-+{
-+ unsigned int pair;
-+ int ret;
-+
-+ for (pair = ETHTOOL_A_CABLE_PAIR_A; pair <= ETHTOOL_A_CABLE_PAIR_D; pair++) {
-+ ret = rtl8224_cable_test_report_pair(phydev, pair);
-+ if (ret == -EBUSY) {
-+ *finished = false;
-+ return 0;
-+ }
-+
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished)
-+{
-+ int ret;
-+
-+ *finished = false;
-+
-+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
-+ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT));
-+ if (ret < 0)
-+ return ret;
-+
-+ if (!(ret & RTL8224_MII_RTCT_DONE))
-+ return 0;
-+
-+ *finished = true;
-+
-+ return rtl8224_cable_test_report(phydev, finished);
-+}
-+
- static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
- {
- int val;
-@@ -1822,11 +2006,14 @@ static struct phy_driver realtek_drvs[]
- }, {
- PHY_ID_MATCH_EXACT(0x001ccad0),
- .name = "RTL8224 2.5Gbps PHY",
-+ .flags = PHY_POLL_CABLE_TEST,
- .get_features = rtl822x_c45_get_features,
- .config_aneg = rtl822x_c45_config_aneg,
- .read_status = rtl822x_c45_read_status,
- .suspend = genphy_c45_pma_suspend,
- .resume = rtlgen_c45_resume,
-+ .cable_test_start = rtl8224_cable_test_start,
-+ .cable_test_get_status = rtl8224_cable_test_get_status,
- }, {
- PHY_ID_MATCH_EXACT(0x001cc961),
- .name = "RTL8366RB Gigabit Ethernet",
--- /dev/null
+From 61958b33ef0bab1c1874c933cd3910f495526782 Mon Sep 17 00:00:00 2001
+Date: Fri, 24 Oct 2025 11:49:00 +0200
+Subject: [PATCH] net: phy: realtek: Add RTL8224 cable testing support
+
+The RTL8224 can detect open pairs and short types (in same pair or some
+other pair). The distance to this problem can be estimated. This is done
+for each of the 4 pairs separately.
+
+It is not meant to be run while there is an active link partner because
+this interferes with the active test pulses.
+
+Output with open 50 m cable:
+
+ Pair A code Open Circuit, source: TDR
+ Pair A, fault length: 51.79m, source: TDR
+ Pair B code Open Circuit, source: TDR
+ Pair B, fault length: 51.28m, source: TDR
+ Pair C code Open Circuit, source: TDR
+ Pair C, fault length: 50.46m, source: TDR
+ Pair D code Open Circuit, source: TDR
+ Pair D, fault length: 51.12m, source: TDR
+
+Terminated cable:
+
+ Pair A code OK, source: TDR
+ Pair B code OK, source: TDR
+ Pair C code OK, source: TDR
+ Pair D code OK, source: TDR
+
+Shorted cable (both short types are at roughly the same distance)
+
+ Pair A code Short to another pair, source: TDR
+ Pair A, fault length: 2.35m, source: TDR
+ Pair B code Short to another pair, source: TDR
+ Pair B, fault length: 2.15m, source: TDR
+ Pair C code OK, source: TDR
+ Pair D code Short within Pair, source: TDR
+ Pair D, fault length: 1.94m, source: TDR
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 187 +++++++++++++++++++++++++
+ 1 file changed, 187 insertions(+)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -8,6 +8,7 @@
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ */
+ #include <linux/bitops.h>
++#include <linux/ethtool_netlink.h>
+ #include <linux/of.h>
+ #include <linux/phy.h>
+ #include <linux/pm_wakeirq.h>
+@@ -127,6 +128,27 @@
+ */
+ #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg))
+
++#define RTL8224_MII_RTCT 0x11
++#define RTL8224_MII_RTCT_ENABLE BIT(0)
++#define RTL8224_MII_RTCT_PAIR_A BIT(4)
++#define RTL8224_MII_RTCT_PAIR_B BIT(5)
++#define RTL8224_MII_RTCT_PAIR_C BIT(6)
++#define RTL8224_MII_RTCT_PAIR_D BIT(7)
++#define RTL8224_MII_RTCT_DONE BIT(15)
++
++#define RTL8224_MII_SRAM_ADDR 0x1b
++#define RTL8224_MII_SRAM_DATA 0x1c
++
++#define RTL8224_SRAM_RTCT_FAULT(pair) (0x8026 + (pair) * 4)
++#define RTL8224_SRAM_RTCT_FAULT_BUSY BIT(0)
++#define RTL8224_SRAM_RTCT_FAULT_OPEN BIT(3)
++#define RTL8224_SRAM_RTCT_FAULT_SAME_SHORT BIT(4)
++#define RTL8224_SRAM_RTCT_FAULT_OK BIT(5)
++#define RTL8224_SRAM_RTCT_FAULT_DONE BIT(6)
++#define RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT BIT(7)
++
++#define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4)
++
+ #define RTL8366RB_POWER_SAVE 0x15
+ #define RTL8366RB_POWER_SAVE_ON BIT(12)
+
+@@ -1453,6 +1475,168 @@ static int rtl822xb_c45_read_status(stru
+ return 0;
+ }
+
++static int rtl8224_cable_test_start(struct phy_device *phydev)
++{
++ u32 val;
++ int ret;
++
++ /* disable auto-negotiation and force 1000/Full */
++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2,
++ RTL822X_VND2_C22_REG(MII_BMCR),
++ BMCR_ANENABLE | BMCR_SPEED100 | BMCR_SPEED10,
++ BMCR_SPEED1000 | BMCR_FULLDPLX);
++ if (ret)
++ return ret;
++
++ mdelay(500);
++
++ /* trigger cable test */
++ val = RTL8224_MII_RTCT_ENABLE;
++ val |= RTL8224_MII_RTCT_PAIR_A;
++ val |= RTL8224_MII_RTCT_PAIR_B;
++ val |= RTL8224_MII_RTCT_PAIR_C;
++ val |= RTL8224_MII_RTCT_PAIR_D;
++
++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
++ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT),
++ RTL8224_MII_RTCT_DONE, val);
++}
++
++static int rtl8224_sram_read(struct phy_device *phydev, u32 reg)
++{
++ int ret;
++
++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
++ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_ADDR),
++ reg);
++ if (ret)
++ return ret;
++
++ return phy_read_mmd(phydev, MDIO_MMD_VEND2,
++ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_DATA));
++}
++
++static int rtl8224_pair_len_get(struct phy_device *phydev, u32 pair)
++{
++ int cable_len;
++ u32 reg_len;
++ int ret;
++ u32 cm;
++
++ reg_len = RTL8224_SRAM_RTCT_LEN(pair);
++
++ ret = rtl8224_sram_read(phydev, reg_len);
++ if (ret < 0)
++ return ret;
++
++ cable_len = ret & 0xff00;
++
++ ret = rtl8224_sram_read(phydev, reg_len + 1);
++ if (ret < 0)
++ return ret;
++
++ cable_len |= (ret & 0xff00) >> 8;
++
++ cable_len -= 620;
++ cable_len = max(cable_len, 0);
++
++ cm = cable_len * 100 / 78;
++
++ return cm;
++}
++
++static int rtl8224_cable_test_result_trans(u32 result)
++{
++ if (!(result & RTL8224_SRAM_RTCT_FAULT_DONE))
++ return -EBUSY;
++
++ if (result & RTL8224_SRAM_RTCT_FAULT_OK)
++ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
++
++ if (result & RTL8224_SRAM_RTCT_FAULT_OPEN)
++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++
++ if (result & RTL8224_SRAM_RTCT_FAULT_SAME_SHORT)
++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
++
++ if (result & RTL8224_SRAM_RTCT_FAULT_BUSY)
++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++
++ if (result & RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT)
++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
++
++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++}
++
++static int rtl8224_cable_test_report_pair(struct phy_device *phydev, unsigned int pair)
++{
++ int fault_rslt;
++ int ret;
++
++ ret = rtl8224_sram_read(phydev, RTL8224_SRAM_RTCT_FAULT(pair));
++ if (ret < 0)
++ return ret;
++
++ fault_rslt = rtl8224_cable_test_result_trans(ret);
++ if (fault_rslt < 0)
++ return 0;
++
++ ret = ethnl_cable_test_result(phydev, pair, fault_rslt);
++ if (ret < 0)
++ return ret;
++
++ switch (fault_rslt) {
++ case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
++ case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
++ case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
++ ret = rtl8224_pair_len_get(phydev, pair);
++ if (ret < 0)
++ return ret;
++
++ return ethnl_cable_test_fault_length(phydev, pair, ret);
++ default:
++ return 0;
++ }
++}
++
++static int rtl8224_cable_test_report(struct phy_device *phydev, bool *finished)
++{
++ unsigned int pair;
++ int ret;
++
++ for (pair = ETHTOOL_A_CABLE_PAIR_A; pair <= ETHTOOL_A_CABLE_PAIR_D; pair++) {
++ ret = rtl8224_cable_test_report_pair(phydev, pair);
++ if (ret == -EBUSY) {
++ *finished = false;
++ return 0;
++ }
++
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished)
++{
++ int ret;
++
++ *finished = false;
++
++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
++ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT));
++ if (ret < 0)
++ return ret;
++
++ if (!(ret & RTL8224_MII_RTCT_DONE))
++ return 0;
++
++ *finished = true;
++
++ return rtl8224_cable_test_report(phydev, finished);
++}
++
+ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
+ {
+ int val;
+@@ -1930,11 +2114,14 @@ static struct phy_driver realtek_drvs[]
+ }, {
+ PHY_ID_MATCH_EXACT(0x001ccad0),
+ .name = "RTL8224 2.5Gbps PHY",
++ .flags = PHY_POLL_CABLE_TEST,
+ .get_features = rtl822x_c45_get_features,
+ .config_aneg = rtl822x_c45_config_aneg,
+ .read_status = rtl822x_c45_read_status,
+ .suspend = genphy_c45_pma_suspend,
+ .resume = rtlgen_c45_resume,
++ .cable_test_start = rtl8224_cable_test_start,
++ .cable_test_get_status = rtl8224_cable_test_get_status,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc961),
+ .name = "RTL8366RB Gigabit Ethernet",
+++ /dev/null
-From 18aa36238a4d835c1644dcccd63d32c7fdd4b310 Mon Sep 17 00:00:00 2001
-Date: Sun, 2 Nov 2025 16:26:37 +0100
-Subject: [PATCH] net: phy: realtek: add interrupt support for RTL8221B
-
-This commit introduces interrupt support for RTL8221B (C45 mode).
-Interrupts are mapped on the VEND2 page. VEND2 registers are only
-accessible via C45 reads and cannot be accessed by C45 over C22.
-
-[Enable only link state change interrupts]
----
- drivers/net/phy/realtek/realtek_main.c | 56 ++++++++++++++++++++++++++
- 1 file changed, 56 insertions(+)
-
---- a/drivers/net/phy/realtek/realtek_main.c
-+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -130,6 +130,11 @@
- */
- #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg))
-
-+#define RTL8221B_VND2_INER 0xa4d2
-+#define RTL8221B_VND2_INER_LINK_STATUS BIT(4)
-+
-+#define RTL8221B_VND2_INSR 0xa4d4
-+
- #define RTL8224_MII_RTCT 0x11
- #define RTL8224_MII_RTCT_ENABLE BIT(0)
- #define RTL8224_MII_RTCT_PAIR_A BIT(4)
-@@ -1772,6 +1777,53 @@ static irqreturn_t rtl9000a_handle_inter
- return IRQ_HANDLED;
- }
-
-+static int rtl8221b_ack_interrupt(struct phy_device *phydev)
-+{
-+ int err;
-+
-+ err = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
-+
-+ return (err < 0) ? err : 0;
-+}
-+
-+static int rtl8221b_config_intr(struct phy_device *phydev)
-+{
-+ int err;
-+
-+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
-+ err = rtl8221b_ack_interrupt(phydev);
-+ if (err)
-+ return err;
-+
-+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
-+ RTL8221B_VND2_INER_LINK_STATUS);
-+ } else {
-+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2,
-+ RTL8221B_VND2_INER, 0);
-+ if (err)
-+ return err;
-+
-+ err = rtl8221b_ack_interrupt(phydev);
-+ }
-+
-+ return err;
-+}
-+
-+static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev)
-+{
-+ int err;
-+
-+ err = rtl8221b_ack_interrupt(phydev);
-+ if (err) {
-+ phy_error(phydev);
-+ return IRQ_NONE;
-+ }
-+
-+ phy_trigger_machine(phydev);
-+
-+ return IRQ_HANDLED;
-+}
-+
- static struct phy_driver realtek_drvs[] = {
- {
- PHY_ID_MATCH_EXACT(0x00008201),
-@@ -1946,6 +1998,8 @@ static struct phy_driver realtek_drvs[]
- }, {
- .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
- .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
-+ .config_intr = rtl8221b_config_intr,
-+ .handle_interrupt = rtl8221b_handle_interrupt,
- .probe = rtl822x_probe,
- .config_init = rtl822xb_config_init,
- .get_rate_matching = rtl822xb_get_rate_matching,
-@@ -1970,6 +2024,8 @@ static struct phy_driver realtek_drvs[]
- }, {
- .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device,
- .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)",
-+ .config_intr = rtl8221b_config_intr,
-+ .handle_interrupt = rtl8221b_handle_interrupt,
- .probe = rtl822x_probe,
- .config_init = rtl822xb_config_init,
- .get_rate_matching = rtl822xb_get_rate_matching,
--- /dev/null
+From 18aa36238a4d835c1644dcccd63d32c7fdd4b310 Mon Sep 17 00:00:00 2001
+Date: Sun, 2 Nov 2025 16:26:37 +0100
+Subject: [PATCH] net: phy: realtek: add interrupt support for RTL8221B
+
+This commit introduces interrupt support for RTL8221B (C45 mode).
+Interrupts are mapped on the VEND2 page. VEND2 registers are only
+accessible via C45 reads and cannot be accessed by C45 over C22.
+
+[Enable only link state change interrupts]
+---
+ drivers/net/phy/realtek/realtek_main.c | 56 ++++++++++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -128,6 +128,11 @@
+ */
+ #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg))
+
++#define RTL8221B_VND2_INER 0xa4d2
++#define RTL8221B_VND2_INER_LINK_STATUS BIT(4)
++
++#define RTL8221B_VND2_INSR 0xa4d4
++
+ #define RTL8224_MII_RTCT 0x11
+ #define RTL8224_MII_RTCT_ENABLE BIT(0)
+ #define RTL8224_MII_RTCT_PAIR_A BIT(4)
+@@ -1880,6 +1885,53 @@ static irqreturn_t rtl9000a_handle_inter
+ return IRQ_HANDLED;
+ }
+
++static int rtl8221b_ack_interrupt(struct phy_device *phydev)
++{
++ int err;
++
++ err = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
++
++ return (err < 0) ? err : 0;
++}
++
++static int rtl8221b_config_intr(struct phy_device *phydev)
++{
++ int err;
++
++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++ err = rtl8221b_ack_interrupt(phydev);
++ if (err)
++ return err;
++
++ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
++ RTL8221B_VND2_INER_LINK_STATUS);
++ } else {
++ err = phy_write_mmd(phydev, MDIO_MMD_VEND2,
++ RTL8221B_VND2_INER, 0);
++ if (err)
++ return err;
++
++ err = rtl8221b_ack_interrupt(phydev);
++ }
++
++ return err;
++}
++
++static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev)
++{
++ int err;
++
++ err = rtl8221b_ack_interrupt(phydev);
++ if (err) {
++ phy_error(phydev);
++ return IRQ_NONE;
++ }
++
++ phy_trigger_machine(phydev);
++
++ return IRQ_HANDLED;
++}
++
+ static struct phy_driver realtek_drvs[] = {
+ {
+ PHY_ID_MATCH_EXACT(0x00008201),
+@@ -2054,6 +2106,8 @@ static struct phy_driver realtek_drvs[]
+ }, {
+ .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
+ .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
++ .config_intr = rtl8221b_config_intr,
++ .handle_interrupt = rtl8221b_handle_interrupt,
+ .probe = rtl822x_probe,
+ .config_init = rtl822xb_config_init,
+ .get_rate_matching = rtl822xb_get_rate_matching,
+@@ -2078,6 +2132,8 @@ static struct phy_driver realtek_drvs[]
+ }, {
+ .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device,
+ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)",
++ .config_intr = rtl8221b_config_intr,
++ .handle_interrupt = rtl8221b_handle_interrupt,
+ .probe = rtl822x_probe,
+ .config_init = rtl822xb_config_init,
+ .get_rate_matching = rtl822xb_get_rate_matching,
--- /dev/null
+From 8e982441ba601d982dd0739972115d85ae01d99b Mon Sep 17 00:00:00 2001
+Date: Tue, 18 Nov 2025 01:40:28 +0200
+Subject: [PATCH] net: phy: realtek: create rtl8211f_config_rgmii_delay()
+
+The control flow in rtl8211f_config_init() has some pitfalls which were
+probably unintended. Specifically it has an early return:
+
+ switch (phydev->interface) {
+ ...
+ default: /* the rest of the modes imply leaving delay as is. */
+ return 0;
+ }
+
+which exits the entire config_init() function. This means it also skips
+doing things such as disabling CLKOUT or disabling PHY-mode EEE.
+
+For the RTL8211FS, which uses PHY_INTERFACE_MODE_SGMII, this might be a
+problem. However, I don't know that it is, so there is no Fixes: tag.
+The issue was observed through code inspection.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 65 +++++++++++++++-----------
+ 1 file changed, 39 insertions(+), 26 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -587,22 +587,11 @@ static int rtl8211c_config_init(struct p
+ CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
+ }
+
+-static int rtl8211f_config_init(struct phy_device *phydev)
++static int rtl8211f_config_rgmii_delay(struct phy_device *phydev)
+ {
+- struct rtl821x_priv *priv = phydev->priv;
+- struct device *dev = &phydev->mdio.dev;
+ u16 val_txdly, val_rxdly;
+ int ret;
+
+- ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1,
+- RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF,
+- priv->phycr1);
+- if (ret < 0) {
+- dev_err(dev, "aldps mode configuration failed: %pe\n",
+- ERR_PTR(ret));
+- return ret;
+- }
+-
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ val_txdly = 0;
+@@ -632,34 +621,58 @@ static int rtl8211f_config_init(struct p
+ RTL8211F_TXCR, RTL8211F_TX_DELAY,
+ val_txdly);
+ if (ret < 0) {
+- dev_err(dev, "Failed to update the TX delay register\n");
++ phydev_err(phydev, "Failed to update the TX delay register: %pe\n",
++ ERR_PTR(ret));
+ return ret;
+ } else if (ret) {
+- dev_dbg(dev,
+- "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n",
+- str_enable_disable(val_txdly));
++ phydev_dbg(phydev,
++ "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n",
++ str_enable_disable(val_txdly));
+ } else {
+- dev_dbg(dev,
+- "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
+- str_enabled_disabled(val_txdly));
++ phydev_dbg(phydev,
++ "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
++ str_enabled_disabled(val_txdly));
+ }
+
+ ret = phy_modify_paged_changed(phydev, RTL8211F_RGMII_PAGE,
+ RTL8211F_RXCR, RTL8211F_RX_DELAY,
+ val_rxdly);
+ if (ret < 0) {
+- dev_err(dev, "Failed to update the RX delay register\n");
++ phydev_err(phydev, "Failed to update the RX delay register: %pe\n",
++ ERR_PTR(ret));
+ return ret;
+ } else if (ret) {
+- dev_dbg(dev,
+- "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n",
+- str_enable_disable(val_rxdly));
++ phydev_dbg(phydev,
++ "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n",
++ str_enable_disable(val_rxdly));
+ } else {
+- dev_dbg(dev,
+- "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n",
+- str_enabled_disabled(val_rxdly));
++ phydev_dbg(phydev,
++ "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n",
++ str_enabled_disabled(val_rxdly));
+ }
+
++ return 0;
++}
++
++static int rtl8211f_config_init(struct phy_device *phydev)
++{
++ struct rtl821x_priv *priv = phydev->priv;
++ struct device *dev = &phydev->mdio.dev;
++ int ret;
++
++ ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1,
++ RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF,
++ priv->phycr1);
++ if (ret < 0) {
++ dev_err(dev, "aldps mode configuration failed: %pe\n",
++ ERR_PTR(ret));
++ return ret;
++ }
++
++ ret = rtl8211f_config_rgmii_delay(phydev);
++ if (ret)
++ return ret;
++
+ if (!priv->has_phycr2)
+ return 0;
+
--- /dev/null
+From 27033d06917758d47162581da7e9de8004049dee Mon Sep 17 00:00:00 2001
+Date: Tue, 18 Nov 2025 01:40:29 +0200
+Subject: [PATCH] net: phy: realtek: eliminate priv->phycr2 variable
+
+The RTL8211F(D)(I)-VD-CG PHY also has support for disabling the CLKOUT,
+and we'd like to introduce the "realtek,clkout-disable" property for
+that.
+
+But it isn't done through the PHYCR2 register, and it becomes awkward to
+have the driver pretend that it is. So just replace the machine-level
+"u16 phycr2" variable with a logical "bool disable_clk_out", which
+scales better to the other PHY as well.
+
+The change is a complete functional equivalent. Before, if the device
+tree property was absent, priv->phycr2 would contain the RTL8211F_CLKOUT_EN
+bit as read from hardware. Now, we don't save priv->phycr2, but we just
+don't call phy_modify_paged() on it. Also, we can simply call
+phy_modify_paged() with the "set" argument to 0.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 38 ++++++++++++++++----------
+ 1 file changed, 23 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -194,8 +194,8 @@ MODULE_LICENSE("GPL");
+
+ struct rtl821x_priv {
+ u16 phycr1;
+- u16 phycr2;
+ bool has_phycr2;
++ bool disable_clk_out;
+ struct clk *clk;
+ /* rtl8211f */
+ u16 iner;
+@@ -266,15 +266,8 @@ static int rtl821x_probe(struct phy_devi
+ priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF;
+
+ priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID);
+- if (priv->has_phycr2) {
+- ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2);
+- if (ret < 0)
+- return ret;
+-
+- priv->phycr2 = ret & RTL8211F_CLKOUT_EN;
+- if (of_property_read_bool(dev->of_node, "realtek,clkout-disable"))
+- priv->phycr2 &= ~RTL8211F_CLKOUT_EN;
+- }
++ priv->disable_clk_out = of_property_read_bool(dev->of_node,
++ "realtek,clkout-disable");
+
+ phydev->priv = priv;
+
+@@ -654,6 +647,23 @@ static int rtl8211f_config_rgmii_delay(s
+ return 0;
+ }
+
++static int rtl8211f_config_clk_out(struct phy_device *phydev)
++{
++ struct rtl821x_priv *priv = phydev->priv;
++ int ret;
++
++ /* The value is preserved if the device tree property is absent */
++ if (!priv->disable_clk_out)
++ return 0;
++
++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
++ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0);
++ if (ret)
++ return ret;
++
++ return genphy_soft_reset(phydev);
++}
++
+ static int rtl8211f_config_init(struct phy_device *phydev)
+ {
+ struct rtl821x_priv *priv = phydev->priv;
+@@ -682,16 +692,14 @@ static int rtl8211f_config_init(struct p
+ if (ret)
+ return ret;
+
+- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
+- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN,
+- priv->phycr2);
+- if (ret < 0) {
++ ret = rtl8211f_config_clk_out(phydev);
++ if (ret) {
+ dev_err(dev, "clkout configuration failed: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+- return genphy_soft_reset(phydev);
++ return 0;
+ }
+
+ static int rtl821x_suspend(struct phy_device *phydev)
--- /dev/null
+From 910ac7bfb1af1ae4cd141ef80e03a6729213c189 Mon Sep 17 00:00:00 2001
+Date: Tue, 18 Nov 2025 01:40:30 +0200
+Subject: [PATCH] net: phy: realtek: eliminate has_phycr2 variable
+
+This variable is assigned in rtl821x_probe() and used in
+rtl8211f_config_init(), which is more complex than it needs to be.
+Simply testing the same condition from rtl821x_probe() in
+rtl8211f_config_init() yields the same result (the PHY driver ID is a
+runtime invariant), but with one temporary variable less.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -194,7 +194,6 @@ MODULE_LICENSE("GPL");
+
+ struct rtl821x_priv {
+ u16 phycr1;
+- bool has_phycr2;
+ bool disable_clk_out;
+ struct clk *clk;
+ /* rtl8211f */
+@@ -245,7 +244,6 @@ static int rtl821x_probe(struct phy_devi
+ {
+ struct device *dev = &phydev->mdio.dev;
+ struct rtl821x_priv *priv;
+- u32 phy_id = phydev->drv->phy_id;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+@@ -265,7 +263,6 @@ static int rtl821x_probe(struct phy_devi
+ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
+ priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF;
+
+- priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID);
+ priv->disable_clk_out = of_property_read_bool(dev->of_node,
+ "realtek,clkout-disable");
+
+@@ -683,7 +680,8 @@ static int rtl8211f_config_init(struct p
+ if (ret)
+ return ret;
+
+- if (!priv->has_phycr2)
++ /* RTL8211FVD has no PHYCR2 register */
++ if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
+ return 0;
+
+ /* Disable PHY-mode EEE so LPI is passed to the MAC */
--- /dev/null
+From e1a31c41bef678afe0d99b7f0dc3711a80c68447 Mon Sep 17 00:00:00 2001
+Date: Tue, 18 Nov 2025 01:40:31 +0200
+Subject: [PATCH] net: phy: realtek: allow CLKOUT to be disabled on
+ RTL8211F(D)(I)-VD-CG
+
+Add CLKOUT disable support for RTL8211F(D)(I)-VD-CG. Like with other PHY
+variants, this feature might be requested by customers when the clock
+output is not used, in order to reduce electromagnetic interference (EMI).
+
+In the common driver, the CLKOUT configuration is done through PHYCR2.
+The RTL_8211FVD_PHYID is singled out as not having that register, and
+execution in rtl8211f_config_init() returns early after commit
+2c67301584f2 ("net: phy: realtek: Avoid PHYCR2 access if PHYCR2 not
+present").
+
+But actually CLKOUT is configured through a different register for this
+PHY. Instead of pretending this is PHYCR2 (which it is not), just add
+some code for modifying this register inside the rtl8211f_disable_clk_out()
+function, and move that outside the code portion that runs only if
+PHYCR2 exists.
+
+In practice this reorders the PHYCR2 writes to disable PHY-mode EEE and
+to disable the CLKOUT for the normal RTL8211F variants, but this should
+be perfectly fine.
+
+It was not noted that RTL8211F(D)(I)-VD-CG would need a genphy_soft_reset()
+call after disabling the CLKOUT. Despite that, we do it out of caution
+and for symmetry with the other RTL8211F models.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 31 ++++++++++++++++++--------
+ 1 file changed, 22 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -90,6 +90,14 @@
+ #define RTL8211F_LEDCR_MASK GENMASK(4, 0)
+ #define RTL8211F_LEDCR_SHIFT 5
+
++/* RTL8211F(D)(I)-VD-CG CLKOUT configuration is specified via magic values
++ * to undocumented register pages. The names here do not reflect the datasheet.
++ * Unlike other PHY models, CLKOUT configuration does not go through PHYCR2.
++ */
++#define RTL8211FVD_CLKOUT_PAGE 0xd05
++#define RTL8211FVD_CLKOUT_REG 0x11
++#define RTL8211FVD_CLKOUT_EN BIT(8)
++
+ /* RTL8211F RGMII configuration */
+ #define RTL8211F_RGMII_PAGE 0xd08
+
+@@ -653,8 +661,13 @@ static int rtl8211f_config_clk_out(struc
+ if (!priv->disable_clk_out)
+ return 0;
+
+- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
+- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0);
++ if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
++ ret = phy_modify_paged(phydev, RTL8211FVD_CLKOUT_PAGE,
++ RTL8211FVD_CLKOUT_REG,
++ RTL8211FVD_CLKOUT_EN, 0);
++ else
++ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
++ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0);
+ if (ret)
+ return ret;
+
+@@ -680,6 +693,13 @@ static int rtl8211f_config_init(struct p
+ if (ret)
+ return ret;
+
++ ret = rtl8211f_config_clk_out(phydev);
++ if (ret) {
++ dev_err(dev, "clkout configuration failed: %pe\n",
++ ERR_PTR(ret));
++ return ret;
++ }
++
+ /* RTL8211FVD has no PHYCR2 register */
+ if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
+ return 0;
+@@ -690,13 +710,6 @@ static int rtl8211f_config_init(struct p
+ if (ret)
+ return ret;
+
+- ret = rtl8211f_config_clk_out(phydev);
+- if (ret) {
+- dev_err(dev, "clkout configuration failed: %pe\n",
+- ERR_PTR(ret));
+- return ret;
+- }
+-
+ return 0;
+ }
+
--- /dev/null
+From bb78b71faf60d11a15f07e3390fcfd31e5e523bb Mon Sep 17 00:00:00 2001
+Date: Tue, 18 Nov 2025 01:40:32 +0200
+Subject: [PATCH] net: phy: realtek: eliminate priv->phycr1 variable
+
+Previous changes have replaced the machine-level priv->phycr2 with a
+high-level priv->disable_clk_out. This created a discrepancy with
+priv->phycr1 which is resolved here, for uniformity.
+
+One advantage of this new implementation is that we don't read
+priv->phycr1 in rtl821x_probe() if we're never going to modify it.
+
+We never test the positive return code from phy_modify_mmd_changed(), so
+we could just as well use phy_modify_mmd().
+
+I took the ALDPS feature description from commit d90db36a9e74 ("net:
+phy: realtek: add dt property to enable ALDPS mode") and transformed it
+into a function comment - the feature is sufficiently non-obvious to
+deserve that.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 44 ++++++++++++++++----------
+ 1 file changed, 28 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -201,7 +201,7 @@ MODULE_AUTHOR("Johnson Leung");
+ MODULE_LICENSE("GPL");
+
+ struct rtl821x_priv {
+- u16 phycr1;
++ bool enable_aldps;
+ bool disable_clk_out;
+ struct clk *clk;
+ /* rtl8211f */
+@@ -252,7 +252,6 @@ static int rtl821x_probe(struct phy_devi
+ {
+ struct device *dev = &phydev->mdio.dev;
+ struct rtl821x_priv *priv;
+- int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -263,14 +262,8 @@ static int rtl821x_probe(struct phy_devi
+ return dev_err_probe(dev, PTR_ERR(priv->clk),
+ "failed to get phy clock\n");
+
+- ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1);
+- if (ret < 0)
+- return ret;
+-
+- priv->phycr1 = ret & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF);
+- if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
+- priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF;
+-
++ priv->enable_aldps = of_property_read_bool(dev->of_node,
++ "realtek,aldps-enable");
+ priv->disable_clk_out = of_property_read_bool(dev->of_node,
+ "realtek,clkout-disable");
+
+@@ -674,17 +667,36 @@ static int rtl8211f_config_clk_out(struc
+ return genphy_soft_reset(phydev);
+ }
+
+-static int rtl8211f_config_init(struct phy_device *phydev)
++/* Advance Link Down Power Saving (ALDPS) mode changes crystal/clock behaviour,
++ * which causes the RXC clock signal to stop for tens to hundreds of
++ * milliseconds.
++ *
++ * Some MACs need the RXC clock to support their internal RX logic, so ALDPS is
++ * only enabled based on an opt-in device tree property.
++ */
++static int rtl8211f_config_aldps(struct phy_device *phydev)
+ {
+ struct rtl821x_priv *priv = phydev->priv;
++ u16 mask = RTL8211F_ALDPS_PLL_OFF |
++ RTL8211F_ALDPS_ENABLE |
++ RTL8211F_ALDPS_XTAL_OFF;
++
++ /* The value is preserved if the device tree property is absent */
++ if (!priv->enable_aldps)
++ return 0;
++
++ return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1,
++ mask, mask);
++}
++
++static int rtl8211f_config_init(struct phy_device *phydev)
++{
+ struct device *dev = &phydev->mdio.dev;
+ int ret;
+
+- ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1,
+- RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF,
+- priv->phycr1);
+- if (ret < 0) {
+- dev_err(dev, "aldps mode configuration failed: %pe\n",
++ ret = rtl8211f_config_aldps(phydev);
++ if (ret) {
++ dev_err(dev, "aldps mode configuration failed: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
--- /dev/null
+From 4465ae435ddc0162d5033a543658449d53d46d08 Mon Sep 17 00:00:00 2001
+Date: Tue, 18 Nov 2025 01:40:33 +0200
+Subject: [PATCH] net: phy: realtek: create rtl8211f_config_phy_eee() helper
+
+To simplify the rtl8211f_config_init() control flow and get rid of
+"early" returns for PHYs where the PHYCR2 register is absent, move the
+entire logic sub-block that deals with disabling PHY-mode EEE to a
+separate function. There, it is much more obvious what the early
+"return 0" skips, and it becomes more difficult to accidentally skip
+unintended stuff.
+
+---
+ drivers/net/phy/realtek/realtek_main.c | 23 ++++++++++++-----------
+ 1 file changed, 12 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -689,6 +689,17 @@ static int rtl8211f_config_aldps(struct
+ mask, mask);
+ }
+
++static int rtl8211f_config_phy_eee(struct phy_device *phydev)
++{
++ /* RTL8211FVD has no PHYCR2 register */
++ if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
++ return 0;
++
++ /* Disable PHY-mode EEE so LPI is passed to the MAC */
++ return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2,
++ RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0);
++}
++
+ static int rtl8211f_config_init(struct phy_device *phydev)
+ {
+ struct device *dev = &phydev->mdio.dev;
+@@ -712,17 +723,7 @@ static int rtl8211f_config_init(struct p
+ return ret;
+ }
+
+- /* RTL8211FVD has no PHYCR2 register */
+- if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
+- return 0;
+-
+- /* Disable PHY-mode EEE so LPI is passed to the MAC */
+- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2,
+- RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0);
+- if (ret)
+- return ret;
+-
+- return 0;
++ return rtl8211f_config_phy_eee(phydev);
+ }
+
+ static int rtl821x_suspend(struct phy_device *phydev)
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1941,6 +1941,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2094,6 +2094,7 @@ static struct phy_driver realtek_drvs[]
}, {
.name = "RTL8226 2.5Gbps PHY",
.match_phy_device = rtl8226_match_phy_device,
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.read_status = rtl822x_read_status,
-@@ -1951,6 +1952,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2104,6 +2105,7 @@ static struct phy_driver realtek_drvs[]
}, {
.match_phy_device = rtl8221b_match_phy_device,
.name = "RTL8226B_RTL8221B 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.config_init = rtl822xb_config_init,
-@@ -1973,6 +1975,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2126,6 +2128,7 @@ static struct phy_driver realtek_drvs[]
}, {
PHY_ID_MATCH_EXACT(0x001cc848),
.name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.config_init = rtl822xb_config_init,
-@@ -1985,6 +1988,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2138,6 +2141,7 @@ static struct phy_driver realtek_drvs[]
}, {
.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
.probe = rtl822x_probe,
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
-@@ -2000,6 +2004,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2153,6 +2157,7 @@ static struct phy_driver realtek_drvs[]
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
.config_intr = rtl8221b_config_intr,
.handle_interrupt = rtl8221b_handle_interrupt,
.probe = rtl822x_probe,
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
-@@ -2011,6 +2016,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2164,6 +2169,7 @@ static struct phy_driver realtek_drvs[]
}, {
.match_phy_device = rtl8221b_vm_cg_c22_match_phy_device,
.name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
.probe = rtl822x_probe,
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
-@@ -2026,6 +2032,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2179,6 +2185,7 @@ static struct phy_driver realtek_drvs[]
.name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)",
.config_intr = rtl8221b_config_intr,
.handle_interrupt = rtl8221b_handle_interrupt,
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1092,8 +1092,8 @@ static int rtl822x_probe(struct phy_devi
+@@ -1245,8 +1245,8 @@ static int rtl822x_probe(struct phy_devi
static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1)
{
bool has_2500, has_sgmii;
has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX,
phydev->host_interfaces) ||
-@@ -1135,18 +1135,42 @@ static int rtl822x_set_serdes_option_mod
+@@ -1288,18 +1288,42 @@ static int rtl822x_set_serdes_option_mod
RTL822X_VND1_SERDES_OPTION,
RTL822X_VND1_SERDES_OPTION_MODE_MASK,
mode);
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1562,9 +1562,11 @@ static bool rtlgen_supports_2_5gbps(stru
+@@ -1715,9 +1715,11 @@ static bool rtlgen_supports_2_5gbps(stru
{
int val;
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -156,6 +156,10 @@
+@@ -162,6 +162,10 @@
#define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4)
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
-@@ -1152,6 +1156,15 @@ static int rtl822x_set_serdes_option_mod
+@@ -1305,6 +1309,15 @@ static int rtl822x_set_serdes_option_mod
return ret;
}
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1619,10 +1619,32 @@ static int rtl8226_match_phy_device(stru
+@@ -1772,10 +1772,32 @@ static int rtl8226_match_phy_device(stru
static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
bool is_c45)
{
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1316,6 +1316,9 @@ static int rtl822x_c45_get_features(stru
+@@ -1469,6 +1469,9 @@ static int rtl822x_c45_get_features(stru
linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT,
phydev->supported);
---
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1196,6 +1196,22 @@ static int rtl822xb_config_init(struct p
+@@ -1349,6 +1349,22 @@ static int rtl822xb_config_init(struct p
return rtl822x_set_serdes_option_mode(phydev, false);
}
static int rtl822xb_get_rate_matching(struct phy_device *phydev,
phy_interface_t iface)
{
-@@ -2070,7 +2086,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2223,7 +2239,7 @@ static struct phy_driver realtek_drvs[]
.handle_interrupt = rtl8221b_handle_interrupt,
.soft_reset = rtl822x_c45_soft_reset,
.probe = rtl822x_probe,
.get_rate_matching = rtl822xb_get_rate_matching,
.get_features = rtl822x_c45_get_features,
.config_aneg = rtl822x_c45_config_aneg,
-@@ -2098,7 +2114,7 @@ static struct phy_driver realtek_drvs[]
+@@ -2251,7 +2267,7 @@ static struct phy_driver realtek_drvs[]
.handle_interrupt = rtl8221b_handle_interrupt,
.soft_reset = rtl822x_c45_soft_reset,
.probe = rtl822x_probe,
---
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1107,6 +1107,11 @@ static int rtl822x_set_serdes_option_mod
+@@ -1260,6 +1260,11 @@ static int rtl822x_set_serdes_option_mod
phydev->host_interfaces) ||
phydev->interface == PHY_INTERFACE_MODE_SGMII;