[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251117191657.4106-1-fabio.baltieri@gmail.com>
Date: Mon, 17 Nov 2025 19:16:57 +0000
From: Fabio Baltieri <fabio.baltieri@...il.com>
To: Heiner Kallweit <hkallweit1@...il.com>,
nic_swsd@...ltek.com,
Andrew Lunn <andrew+netdev@...n.ch>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>
Cc: netdev@...r.kernel.org,
linux-kernel@...r.kernel.org,
Fabio Baltieri <fabio.baltieri@...il.com>
Subject: [PATCH] r8169: add support for RTL8127ATF
Add support for ATF variant of the RTL8127, this uses an SFP transceiver
instead of the twisted pair one and only runs at 1Gbps or 10Gbps.
The change is based on the r8127 driver package version 11.015.00
available on the Realtek website and also on
https://github.com/openwrt/rtl8127.
There's no public datasheet for the chip so this is just porting over
the original vendor code to the API and style of the upstream one.
Signed-off-by: Fabio Baltieri <fabio.baltieri@...il.com>
---
Hi, this is a port of the fiber specific configuration code found in the
Realtek r8127 driver to the upstream one, basically just configuring the
serdes and making sure eee is disabled for good when the specific chip
variant is detected.
The code is full of magics and there's no public datasheet for the chip
so not much space to clarify what the individual registers/bits do, but
that seems to be the norm for this driver.
Tested this with a pair of RTL8127ATF based cards connected to each
other directly both with a 10G fiber transceivers and DAC cable and with
the other end running either the same r8169 upstream driver or the
Realtek r8127 one. Tested both 10G and 1G speed, connection seems to be
stable in all combinations.
That's all the 10G gear I have, though I also run this in a system with
other r8169 based cards just in case and things seems to work as
expected.
Cheers,
Fabio
---
drivers/net/ethernet/realtek/r8169_main.c | 179 +++++++++++++++++++++-
1 file changed, 176 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index d18734fe12e..7676c2af90e 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -729,6 +729,7 @@ struct rtl8169_private {
unsigned supports_gmii:1;
unsigned aspm_manageable:1;
unsigned dash_enabled:1;
+ unsigned fiber_mode:1;
dma_addr_t counters_phys_addr;
struct rtl8169_counters *counters;
struct rtl8169_tc_offsets tc_offset;
@@ -842,7 +843,8 @@ static bool rtl_supports_eee(struct rtl8169_private *tp)
{
return tp->mac_version >= RTL_GIGA_MAC_VER_34 &&
tp->mac_version != RTL_GIGA_MAC_VER_37 &&
- tp->mac_version != RTL_GIGA_MAC_VER_39;
+ tp->mac_version != RTL_GIGA_MAC_VER_39 &&
+ !tp->fiber_mode;
}
static void rtl_read_mac_from_reg(struct rtl8169_private *tp, u8 *mac, int reg)
@@ -1399,6 +1401,119 @@ DECLARE_RTL_COND(rtl_ocp_tx_cond)
return RTL_R8(tp, IBISR0) & 0x20;
}
+#define R8127_SDS_CMD 0x2348
+#define R8127_SDS_ADDR 0x234a
+#define R8127_SDS_DATA_IN 0x234c
+#define R8127_SDS_DATA_OUT 0x234e
+
+#define R8127_MAKE_SDS_ADDR(_index, _page, _reg) \
+ (((_index) << 11) | ((_page) << 5) | (_reg))
+
+#define R8127_SDS_CMD_IN BIT(0)
+#define R8127_SDS_WE_IN BIT(1)
+
+DECLARE_RTL_COND(rtl_sds_cmd_done)
+{
+ return RTL_R16(tp, R8127_SDS_CMD) & R8127_SDS_CMD_IN;
+}
+
+static u16 rtl8127_sds_phy_read(struct rtl8169_private *tp,
+ u16 index, u16 page, u16 reg)
+{
+ RTL_W16(tp, R8127_SDS_ADDR, R8127_MAKE_SDS_ADDR(index, page, reg));
+ RTL_W16(tp, R8127_SDS_CMD, R8127_SDS_CMD_IN);
+
+ if (rtl_loop_wait_low(tp, &rtl_sds_cmd_done, 1, 100))
+ return RTL_R16(tp, R8127_SDS_DATA_OUT);
+ else
+ return 0xffff;
+}
+
+static void rtl8127_sds_phy_write(struct rtl8169_private *tp,
+ u16 index, u16 page, u16 reg, u16 val)
+{
+ RTL_W16(tp, R8127_SDS_DATA_IN, val);
+ RTL_W16(tp, R8127_SDS_ADDR, R8127_MAKE_SDS_ADDR(index, page, reg));
+ RTL_W16(tp, R8127_SDS_CMD, R8127_SDS_CMD_IN | R8127_SDS_WE_IN);
+
+ rtl_loop_wait_low(tp, &rtl_sds_cmd_done, 1, 100);
+}
+
+static void rtl8127_sds_phy_modify(struct rtl8169_private *tp,
+ u16 index, u16 page, u16 addr,
+ u16 mask, u16 set)
+{
+ u16 val;
+
+ val = rtl8127_sds_phy_read(tp, index, page, addr);
+ val = (val & ~mask) | set;
+ rtl8127_sds_phy_write(tp, index, page, addr, val);
+}
+
+static void rtl8127_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));
+ udelay(10);
+}
+
+static void rtl8127_sds_phy_exit_1g(struct rtl8169_private *tp)
+{
+ rtl8127_sds_phy_modify(tp, 0, 1, 31, BIT(3), 0);
+ rtl8127_sds_phy_modify(tp, 0, 2, 0,
+ BIT(13) | BIT(12) | BIT(6),
+ BIT(6));
+
+ rtl8127_sds_phy_reset(tp);
+}
+
+static void rtl8127_set_sds_phy_caps_1g(struct rtl8169_private *tp)
+{
+ u16 val;
+
+ rtl8127_sds_phy_modify(tp, 0, 1, 31, 0, BIT(3));
+ rtl8127_sds_phy_modify(tp, 0, 2, 0,
+ BIT(13) | BIT(12) | BIT(6),
+ BIT(12) | BIT(6));
+ RTL_W16(tp, 0x233a, 0x8004);
+
+ val = RTL_R16(tp, 0x233e);
+ val &= BIT(13) | BIT(12) | BIT(1) | BIT(0);
+ val |= BIT(1);
+ RTL_W16(tp, 0x233e, val);
+
+ r8169_mdio_write(tp, 0xc40a, 0x0);
+ r8169_mdio_write(tp, 0xc466, 0x0);
+ r8169_mdio_write(tp, 0xc808, 0x0);
+ r8169_mdio_write(tp, 0xc80a, 0x0);
+
+ val = r8168_phy_ocp_read(tp, 0xc804);
+ r8168_phy_ocp_write(tp, 0xc804, (val & ~0x000f) | 0x000c);
+}
+
+static void rtl8127_set_sds_phy_caps_10g(struct rtl8169_private *tp)
+{
+ u16 val;
+
+ RTL_W16(tp, 0x233a, 0x801a);
+
+ val = RTL_R16(tp, 0x233e);
+ val &= BIT(13) | BIT(12) | BIT(1) | BIT(0);
+ val |= BIT(12);
+ RTL_W16(tp, 0x233e, val);
+
+ r8169_mdio_write(tp, 0xc40a, 0x0);
+ r8169_mdio_write(tp, 0xc466, 0x3);
+ r8169_mdio_write(tp, 0xc808, 0x0);
+ r8169_mdio_write(tp, 0xc80a, 0x0);
+
+ val = r8168_phy_ocp_read(tp, 0xc804);
+ r8168_phy_ocp_write(tp, 0xc804, (val & ~0x000f) | 0x000c);
+}
+
static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
{
RTL_W8(tp, IBCR2, RTL_R8(tp, IBCR2) & ~0x01);
@@ -1512,6 +1627,15 @@ static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
}
}
+static bool rtl_fiber_mode(struct rtl8169_private *tp)
+{
+ if (tp->mac_version == RTL_GIGA_MAC_VER_80 &&
+ (r8168_mac_ocp_read(tp, 0xd006) & 0xff) == 0x07)
+ return true;
+
+ return false;
+}
+
static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable)
{
if (tp->mac_version >= RTL_GIGA_MAC_VER_25 &&
@@ -2293,6 +2417,27 @@ 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);
+
+ if (tp->fiber_mode) {
+ if (cmd->base.speed != SPEED_1000 &&
+ cmd->base.speed != SPEED_10000)
+ return -EINVAL;
+
+ rtl8127_sds_phy_exit_1g(tp);
+
+ if (cmd->base.speed == SPEED_1000)
+ rtl8127_set_sds_phy_caps_1g(tp);
+ else
+ rtl8127_set_sds_phy_caps_10g(tp);
+ }
+
+ return phy_ethtool_set_link_ksettings(ndev, cmd);
+}
+
static const struct ethtool_ops rtl8169_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES,
@@ -2312,7 +2457,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,
@@ -2390,7 +2535,10 @@ static void rtl8125a_config_eee_mac(struct rtl8169_private *tp)
static void rtl8125b_config_eee_mac(struct rtl8169_private *tp)
{
- r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
+ if (tp->fiber_mode)
+ r8168_mac_ocp_modify(tp, 0xe040, BIT(1) | BIT(0), 0);
+ else
+ r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
}
static void rtl_rar_exgmac_set(struct rtl8169_private *tp, const u8 *addr)
@@ -2440,6 +2588,22 @@ 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->fiber_mode) {
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT);
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT);
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_100baseT_Full_BIT);
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_2500baseT_Full_BIT);
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_5000baseT_Full_BIT);
+
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_Autoneg_BIT);
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_Pause_BIT);
+ phy_remove_link_mode(tp->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT);
+
+ tp->phydev->autoneg = 0;
+ tp->phydev->port = PORT_FIBRE;
+ }
+
/* We may have called phy_speed_down before */
phy_speed_up(tp->phydev);
@@ -4893,6 +5057,13 @@ static int rtl_open(struct net_device *dev)
goto err_free_irq;
rtl8169_up(tp);
+
+ if (tp->fiber_mode) {
+ rtl8127_sds_phy_exit_1g(tp);
+
+ rtl8127_set_sds_phy_caps_10g(tp);
+ }
+
rtl8169_init_counter_offsets(tp);
netif_start_queue(dev);
out:
@@ -5453,6 +5624,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->dash_type = rtl_get_dash_type(tp);
tp->dash_enabled = rtl_dash_is_enabled(tp);
+ tp->fiber_mode = rtl_fiber_mode(tp);
+
tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 &&
--
2.47.3
Powered by blists - more mailing lists