[PATCH net-next v2 3/4] net: phy: realtek: add support for RTL8261C_CG
From: javen
Date: Thu Jun 04 2026 - 03:47:23 EST
From: Javen Xu <javen_xu@xxxxxxxxxxxxxx>
This patch adds support for Realtek phy chip RTL8261C_CG. Its PHY ID is
0x001cc898.
Signed-off-by: Javen Xu <javen_xu@xxxxxxxxxxxxxx>
---
Changes in v2:
- remove RTL8261D, only support RTL8261C_CG
- remove speed 500, rarely used
- add helper function, genphy_c45_soft_reset() and genphy_c45_config_master_slave()
---
drivers/net/phy/realtek/realtek_main.c | 178 +++++++++++++++++++++++++
1 file changed, 178 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 27268811f564..86788feaae92 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -141,6 +141,10 @@
#define RTL8211F_PHYSICAL_ADDR_WORD1 17
#define RTL8211F_PHYSICAL_ADDR_WORD2 18
+#define RTL8261X_EXT_ADDR_REG 0xa436
+#define RTL8261X_EXT_DATA_REG 0xa438
+#define RTL_8261X_SUB_PHY_ID_ADDR 0x801d
+
#define RTL822X_VND1_SERDES_OPTION 0x697a
#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0
@@ -251,6 +255,33 @@
#define RTL_8221B_VM_CG 0x001cc84a
#define RTL_8251B 0x001cc862
#define RTL_8261C 0x001cc890
+#define RTL_8261C_CG 0x001cc898
+
+#define RTL8261C_CE_MODEL 0x00
+#define RTL8261X_GBCR_REG 0xa412
+#define RTL8261X_IMR 0xa4d2
+#define RTL8261X_ISR 0xa4d4
+#define RTL8261X_INT_AUTONEG_ERROR BIT(0)
+#define RTL8261X_INT_PAGE_RECV BIT(2)
+#define RTL8261X_INT_AUTONEG_DONE BIT(3)
+#define RTL8261X_INT_LINK_CHG BIT(4)
+#define RTL8261X_INT_PHY_REG_ACCESS BIT(5)
+#define RTL8261X_INT_PME BIT(7)
+#define RTL8261X_INT_ALDPS_CHG BIT(9)
+#define RTL8261X_INT_JABBER BIT(10)
+
+#define RTL8261X_INT_MASK_DEFAULT (RTL8261X_INT_AUTONEG_DONE | \
+ RTL8261X_INT_LINK_CHG)
+
+#define RTL8261X_INT_MASK_ALL (RTL8261X_INT_AUTONEG_ERROR | \
+ RTL8261X_INT_PAGE_RECV | \
+ RTL8261X_INT_AUTONEG_DONE | \
+ RTL8261X_INT_LINK_CHG | \
+ RTL8261X_INT_PHY_REG_ACCESS | \
+ RTL8261X_INT_PME | \
+ RTL8261X_INT_ALDPS_CHG | \
+ RTL8261X_INT_JABBER)
+
/* RTL8211E and RTL8211F support up to three LEDs */
#define RTL8211x_LED_COUNT 3
@@ -270,6 +301,10 @@ struct rtl821x_priv {
u16 iner;
};
+struct rtl8261x_priv {
+ u8 sub_phy_id;
+};
+
static int rtl821x_read_page(struct phy_device *phydev)
{
return __phy_read(phydev, RTL821x_PAGE_SELECT);
@@ -310,6 +345,137 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
return phy_restore_page(phydev, oldpage, ret);
}
+static int rtl8261x_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct rtl8261x_priv *priv;
+ int sub_phy_id, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
+ RTL_8261X_SUB_PHY_ID_ADDR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG);
+ if (ret < 0)
+ return ret;
+
+ sub_phy_id = (ret >> 8) & 0xff;
+ priv->sub_phy_id = sub_phy_id;
+
+ switch (sub_phy_id) {
+ case RTL8261C_CE_MODEL:
+ phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
+ break;
+
+ default:
+ phydev_err(phydev, "Unknown sub_id 0x%02x\n", sub_phy_id);
+ return -ENODEV;
+ }
+ phydev->priv = priv;
+
+ return 0;
+}
+
+static int rtl8261x_get_features(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_c45_pma_read_abilities(phydev);
+ if (ret)
+ return ret;
+ /*
+ * Supplement Multi-Gig speeds that may not be automatically detected
+ * RTL8261X supports 2.5G/5G in addition to standard 10G
+ */
+ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ phydev->supported);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+ phydev->supported);
+
+ return 0;
+}
+
+static int rtl8261x_config_intr(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_ISR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_IMR,
+ RTL8261X_INT_MASK_DEFAULT);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_IMR, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t rtl8261x_handle_interrupt(struct phy_device *phydev)
+{
+ int irq_status;
+
+ irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_ISR);
+ if (irq_status < 0) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ if (!(irq_status & RTL8261X_INT_MASK_ALL))
+ return IRQ_NONE;
+
+ if (irq_status & (RTL8261X_INT_LINK_CHG | RTL8261X_INT_AUTONEG_DONE |
+ RTL8261X_INT_AUTONEG_ERROR | RTL8261X_INT_JABBER))
+ phy_trigger_machine(phydev);
+
+ return IRQ_HANDLED;
+}
+
+static int rtl8261x_config_aneg(struct phy_device *phydev)
+{
+ bool changed = false;
+ u16 adv_1g = 0;
+ int ret;
+
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ return genphy_c45_pma_setup_forced(phydev);
+
+ ret = genphy_c45_config_master_slave(phydev);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
+ ret = genphy_c45_config_aneg(phydev);
+ if (ret < 0)
+ return ret;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->advertising))
+ adv_1g = ADVERTISE_1000FULL;
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
+ RTL8261X_GBCR_REG,
+ ADVERTISE_1000FULL, adv_1g);
+ if (ret < 0)
+ return ret;
+ if (ret > 0 || changed)
+ return genphy_c45_restart_aneg(phydev);
+
+ return 0;
+}
+
static int rtl821x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -3001,6 +3167,18 @@ static struct phy_driver realtek_drvs[] = {
.resume = genphy_resume,
.read_mmd = genphy_read_mmd_unsupported,
.write_mmd = genphy_write_mmd_unsupported,
+ }, {
+ PHY_ID_MATCH_EXACT(RTL_8261C_CG),
+ .name = "Realtek RTL8261C 10Gbps PHY",
+ .probe = rtl8261x_probe,
+ .get_features = rtl8261x_get_features,
+ .config_aneg = rtl8261x_config_aneg,
+ .read_status = genphy_c45_read_status,
+ .config_intr = rtl8261x_config_intr,
+ .handle_interrupt = rtl8261x_handle_interrupt,
+ .soft_reset = genphy_c45_soft_reset,
+ .suspend = genphy_c45_pma_suspend,
+ .resume = genphy_c45_pma_resume,
},
};
--
2.43.0