From c4277d21ab694c7964a48759a5452e5bbbe12965 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 10 Jan 2026 16:14:05 +0100 Subject: [PATCH 1/2] net: phy: realtek: add dummy PHY driver for RTL8127ATF RTL8127ATF supports a SFP+ port for fiber modules (10GBASE-SR/LR/ER/ZR and DAC). The list of supported modes was provided by Realtek. According to the r8127 vendor driver also 1G modules are supported, but this needs some more complexity in the driver, and only 10G mode has been tested so far. Therefore mainline support will be limited to 10G for now. The SFP port signals are hidden in the chip IP and driven by firmware. Therefore mainline SFP support can't be used here. This PHY driver is used by the RTL8127ATF support in r8169. RTL8127ATF reports the same PHY ID as the TP version. Therefore use a dummy PHY ID. This PHY driver is used by the RTL8127ATF support in r8169. Signed-off-by: Heiner Kallweit Link: https://patch.msgid.link/e3d55162-210a-4fab-9abf-99c6954eee10@gmail.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 + drivers/net/phy/realtek/realtek_main.c | 54 ++++++++++++++++++++++++++ include/net/phy/realtek_phy.h | 7 ++++ 3 files changed, 62 insertions(+) create mode 100644 include/net/phy/realtek_phy.h diff --git a/MAINTAINERS b/MAINTAINERS index 12c03e61526b..6348b87b7600 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9417,6 +9417,7 @@ F: include/linux/phy_link_topology.h F: include/linux/phylib_stubs.h F: include/linux/platform_data/mdio-bcm-unimac.h F: include/linux/platform_data/mdio-gpio.h +F: include/net/phy/ F: include/trace/events/mdio.h F: include/uapi/linux/mdio.h F: include/uapi/linux/mii.h diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index eb5b540ada0e..5a7f472bf58e 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "../phylib.h" #include "realtek.h" @@ -2100,6 +2101,45 @@ static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev) return IRQ_HANDLED; } +static int rtlgen_sfp_get_features(struct phy_device *phydev) +{ + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + phydev->supported); + + /* set default mode */ + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + + phydev->port = PORT_FIBRE; + + return 0; +} + +static int rtlgen_sfp_read_status(struct phy_device *phydev) +{ + int val, err; + + err = genphy_update_link(phydev); + if (err) + return err; + + if (!phydev->link) + return 0; + + val = rtlgen_read_vend2(phydev, RTL_VND2_PHYSR); + if (val < 0) + return val; + + rtlgen_decode_physr(phydev, val); + + return 0; +} + +static int rtlgen_sfp_config_aneg(struct phy_device *phydev) +{ + return 0; +} + static struct phy_driver realtek_drvs[] = { { PHY_ID_MATCH_EXACT(0x00008201), @@ -2361,6 +2401,20 @@ static struct phy_driver realtek_drvs[] = { .write_page = rtl821x_write_page, .read_mmd = rtl822x_read_mmd, .write_mmd = rtl822x_write_mmd, + }, { + PHY_ID_MATCH_EXACT(PHY_ID_RTL_DUMMY_SFP), + .name = "Realtek SFP PHY Mode", + .flags = PHY_IS_INTERNAL, + .probe = rtl822x_probe, + .get_features = rtlgen_sfp_get_features, + .config_aneg = rtlgen_sfp_config_aneg, + .read_status = rtlgen_sfp_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .read_mmd = rtl822x_read_mmd, + .write_mmd = rtl822x_write_mmd, }, { PHY_ID_MATCH_EXACT(0x001ccad0), .name = "RTL8224 2.5Gbps PHY", diff --git a/include/net/phy/realtek_phy.h b/include/net/phy/realtek_phy.h new file mode 100644 index 000000000000..d683bc1b0659 --- /dev/null +++ b/include/net/phy/realtek_phy.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _REALTEK_PHY_H +#define _REALTEK_PHY_H + +#define PHY_ID_RTL_DUMMY_SFP 0x001ccbff + +#endif /* _REALTEK_PHY_H */ From fef0f545511f72223481aab1fd24d5f8f1c9d774 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 10 Jan 2026 16:15:32 +0100 Subject: [PATCH 2/2] r8169: add support for RTL8127ATF (Fiber SFP) RTL8127ATF supports a SFP+ port for fiber modules (10GBASE-SR/LR/ER/ZR and DAC). The list of supported modes was provided by Realtek. According to the r8127 vendor driver also 1G modules are supported, but this needs some more complexity in the driver, and only 10G mode has been tested so far. Therefore mainline support will be limited to 10G for now. The SFP port signals are hidden in the chip IP and driven by firmware. Therefore mainline SFP support can't be used here. Signed-off-by: Heiner Kallweit Tested-by: Fabio Baltieri Link: https://patch.msgid.link/5c390273-458f-4d92-896b-3d85f2998d7d@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/realtek/r8169_main.c | 89 ++++++++++++++++++++++- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 755083852eef..faf910ecf81a 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "r8169.h" #include "r8169_firmware.h" @@ -733,6 +734,7 @@ struct rtl8169_private { unsigned supports_gmii:1; unsigned aspm_manageable:1; unsigned dash_enabled:1; + bool sfp_mode:1; dma_addr_t counters_phys_addr; struct rtl8169_counters *counters; struct rtl8169_tc_offsets tc_offset; @@ -1097,6 +1099,10 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg) if (rtl_ocp_reg_failure(reg)) return 0; + /* Return dummy MII_PHYSID2 in SFP mode to match SFP PHY driver */ + if (tp->sfp_mode && reg == (OCP_STD_PHY_BASE + 2 * MII_PHYSID2)) + return PHY_ID_RTL_DUMMY_SFP & 0xffff; + RTL_W32(tp, GPHY_OCP, reg << 15); return rtl_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ? @@ -1154,6 +1160,46 @@ static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask, raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); } +static void r8127_sfp_sds_phy_reset(struct rtl8169_private *tp) +{ + RTL_W8(tp, 0x2350, RTL_R8(tp, 0x2350) & ~BIT(0)); + udelay(1); + + RTL_W16(tp, 0x233a, 0x801f); + RTL_W8(tp, 0x2350, RTL_R8(tp, 0x2350) | BIT(0)); + usleep_range(10, 20); +} + +static void r8127_sfp_init_10g(struct rtl8169_private *tp) +{ + int val; + + r8127_sfp_sds_phy_reset(tp); + + RTL_W16(tp, 0x233a, 0x801a); + RTL_W16(tp, 0x233e, (RTL_R16(tp, 0x233e) & ~0x3003) | 0x1000); + + r8168_phy_ocp_write(tp, 0xc40a, 0x0000); + r8168_phy_ocp_write(tp, 0xc466, 0x0003); + r8168_phy_ocp_write(tp, 0xc808, 0x0000); + r8168_phy_ocp_write(tp, 0xc80a, 0x0000); + + val = r8168_phy_ocp_read(tp, 0xc804); + r8168_phy_ocp_write(tp, 0xc804, (val & ~0x000f) | 0x000c); +} + +static void rtl_sfp_init(struct rtl8169_private *tp) +{ + if (tp->mac_version == RTL_GIGA_MAC_VER_80) + r8127_sfp_init_10g(tp); +} + +static void rtl_sfp_reset(struct rtl8169_private *tp) +{ + if (tp->mac_version == RTL_GIGA_MAC_VER_80) + r8127_sfp_sds_phy_reset(tp); +} + /* Work around a hw issue with RTL8168g PHY, the quirk disables * PHY MCU interrupts before PHY power-down. */ @@ -2308,6 +2354,36 @@ static void rtl8169_get_eth_ctrl_stats(struct net_device *dev, le32_to_cpu(tp->counters->rx_unknown_opcode); } +static int rtl8169_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct rtl8169_private *tp = netdev_priv(ndev); + struct phy_device *phydev = tp->phydev; + int duplex = cmd->base.duplex; + int speed = cmd->base.speed; + + if (!tp->sfp_mode) + return phy_ethtool_ksettings_set(phydev, cmd); + + if (cmd->base.autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (!phy_check_valid(speed, duplex, phydev->supported)) + return -EINVAL; + + mutex_lock(&phydev->lock); + + phydev->autoneg = AUTONEG_DISABLE; + phydev->speed = speed; + phydev->duplex = duplex; + + rtl_sfp_init(tp); + + mutex_unlock(&phydev->lock); + + return 0; +} + static const struct ethtool_ops rtl8169_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES, @@ -2327,7 +2403,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_eee = rtl8169_get_eee, .set_eee = rtl8169_set_eee, .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, + .set_link_ksettings = rtl8169_set_link_ksettings, .get_ringparam = rtl8169_get_ringparam, .get_pause_stats = rtl8169_get_pause_stats, .get_pauseparam = rtl8169_get_pauseparam, @@ -2435,6 +2511,9 @@ static void rtl8169_init_phy(struct rtl8169_private *tp) tp->pci_dev->subsystem_device == 0xe000) phy_write_paged(tp->phydev, 0x0001, 0x10, 0xf01b); + if (tp->sfp_mode) + rtl_sfp_init(tp); + /* We may have called phy_speed_down before */ phy_speed_up(tp->phydev); @@ -4800,6 +4879,10 @@ static void rtl8169_down(struct rtl8169_private *tp) phy_stop(tp->phydev); + /* Reset SerDes PHY to bring down fiber link */ + if (tp->sfp_mode) + rtl_sfp_reset(tp); + rtl8169_update_counters(tp); pci_clear_master(tp->pci_dev); @@ -5459,13 +5542,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } tp->aspm_manageable = !rc; - /* Fiber mode on RTL8127AF isn't supported */ if (rtl_is_8125(tp)) { u16 data = r8168_mac_ocp_read(tp, 0xd006); if ((data & 0xff) == 0x07) - return dev_err_probe(&pdev->dev, -ENODEV, - "Fiber mode not supported\n"); + tp->sfp_mode = true; } tp->dash_type = rtl_get_dash_type(tp);