[PATCH net-next v1 3/5] r8169: fix RTL8116af link readiness bug
From: javen
Date: Wed Jun 03 2026 - 03:16:32 EST
From: Javen Xu <javen_xu@xxxxxxxxxxxxxx>
For saving power, RTL8116af can not read link status from standard phy
register. Instead, we should read link status through mac register.
Signed-off-by: Javen Xu <javen_xu@xxxxxxxxxxxxxx>
---
drivers/net/ethernet/realtek/r8169_main.c | 73 +++++++++++++++++++----
1 file changed, 61 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 1e2e4074d343..56653608633a 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -454,6 +454,12 @@ enum rtl8125_registers {
#define RX_FETCH_DFLT_8125 (8 << 27)
+#define OCP_SDS_ADDR_REG 0xEB10
+#define OCP_SDS_CMD_REG 0xEB0E
+#define OCP_SDS_DATA_REG 0xEB14
+#define SDS_CMD_READ 0x0001
+#define RTL_SDS_C22_BASE 0x40
+
enum rtl_register_content {
/* InterruptStatusBits */
SYSErr = 0x8000,
@@ -728,6 +734,12 @@ enum rtl_dash_type {
RTL_DASH_25_BP,
};
+enum rtl_sfp_mode {
+ RTL_SFP_NONE,
+ RTL_SFP_8116_AF,
+ RTL_SFP_8127_ATF,
+};
+
struct rtl8169_private {
void __iomem *mmio_addr; /* memory map physical address */
struct pci_dev *pci_dev;
@@ -736,6 +748,7 @@ struct rtl8169_private {
struct napi_struct napi;
enum mac_version mac_version;
enum rtl_dash_type dash_type;
+ enum rtl_sfp_mode sfp_mode;
u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
u32 dirty_tx;
@@ -762,7 +775,6 @@ 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;
@@ -1195,13 +1207,42 @@ static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
return val;
}
+static u16 rtl8116af_sds_read(struct rtl8169_private *tp, u16 sds_reg)
+{
+ r8168_mac_ocp_write(tp, OCP_SDS_ADDR_REG, sds_reg);
+ r8168_mac_ocp_write(tp, OCP_SDS_CMD_REG, SDS_CMD_READ);
+ return r8168_mac_ocp_read(tp, OCP_SDS_DATA_REG);
+}
+
+static bool rtl_is_8116af(struct rtl8169_private *tp)
+{
+ return tp->mac_version == RTL_GIGA_MAC_VER_52 &&
+ (r8168_mac_ocp_read(tp, 0xdc00) & 0x0078) == 0x0030 &&
+ (r8168_mac_ocp_read(tp, 0xd006) & 0x00ff) == 0x0000;
+}
+
static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
{
if (rtl_ocp_reg_failure(reg))
return 0;
+ if (tp->sfp_mode == RTL_SFP_8116_AF) {
+ switch (reg) {
+ case OCP_STD_PHY_BASE + 2 * MII_PHYSID1:
+ return upper_16_bits(PHY_ID_RTL8116AF_DUMMY);
+ case OCP_STD_PHY_BASE + 2 * MII_PHYSID2:
+ return lower_16_bits(PHY_ID_RTL8116AF_DUMMY);
+ case OCP_STD_PHY_BASE + 2 * MII_BMSR:
+ return rtl8116af_sds_read(tp, RTL_SDS_C22_BASE + MII_BMSR);
+ case OCP_STD_PHY_BASE + 2 * MII_BMCR:
+ return rtl8116af_sds_read(tp, RTL_SDS_C22_BASE + MII_BMCR);
+ default:
+ break;
+ }
+ }
+
/* Return dummy MII_PHYSID2 in SFP mode to match SFP PHY driver */
- if (tp->sfp_mode && reg == (OCP_STD_PHY_BASE + 2 * MII_PHYSID2))
+ if (tp->sfp_mode == RTL_SFP_8127_ATF && reg == (OCP_STD_PHY_BASE + 2 * MII_PHYSID2))
return PHY_ID_RTL_DUMMY_SFP & 0xffff;
RTL_W32(tp, GPHY_OCP, reg << 15);
@@ -1578,6 +1619,20 @@ static bool rtl_dash_is_enabled(struct rtl8169_private *tp)
}
}
+static enum rtl_sfp_mode rtl_get_sfp_mode(struct rtl8169_private *tp)
+{
+ if (rtl_is_8125(tp)) {
+ u16 data = r8168_mac_ocp_read(tp, 0xd006);
+
+ if ((data & 0xff) == 0x07)
+ return RTL_SFP_8127_ATF;
+ } else if (rtl_is_8116af(tp)) {
+ return RTL_SFP_8116_AF;
+ }
+
+ return RTL_SFP_NONE;
+}
+
static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
@@ -2394,7 +2449,7 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
int duplex = cmd->base.duplex;
int speed = cmd->base.speed;
- if (!tp->sfp_mode)
+ if (tp->sfp_mode != RTL_SFP_8127_ATF)
return phy_ethtool_ksettings_set(phydev, cmd);
if (cmd->base.autoneg != AUTONEG_DISABLE)
@@ -2552,7 +2607,7 @@ 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)
+ if (tp->sfp_mode == RTL_SFP_8127_ATF)
rtl_sfp_init(tp);
/* We may have called phy_speed_down before */
@@ -5010,7 +5065,7 @@ static void rtl8169_down(struct rtl8169_private *tp)
phy_stop(tp->phydev);
/* Reset SerDes PHY to bring down fiber link */
- if (tp->sfp_mode)
+ if (tp->sfp_mode == RTL_SFP_8127_ATF)
rtl_sfp_reset(tp);
rtl8169_update_counters(tp);
@@ -5679,13 +5734,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
tp->aspm_manageable = !rc;
- if (rtl_is_8125(tp)) {
- u16 data = r8168_mac_ocp_read(tp, 0xd006);
-
- if ((data & 0xff) == 0x07)
- tp->sfp_mode = true;
- }
-
+ tp->sfp_mode = rtl_get_sfp_mode(tp);
tp->dash_type = rtl_get_dash_type(tp);
tp->dash_enabled = rtl_dash_is_enabled(tp);
--
2.43.0