mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next v2 0/4] Add support for RTL8261C_CG
@ 2026-06-04  7:42 javen
  2026-06-04  7:42 ` [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: javen @ 2026-06-04  7:42 UTC (permalink / raw)
  To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
  Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu

From: Javen Xu <javen_xu@realsil.com.cn>

Add support for RTL8261C_CG and add support for loading firmware.

Javen Xu (4):
  net: phy: phy-c45: add genphy_c45_soft_reset()
  net: phy: phy-c45: add genphy_c45_config_master_slave()
  net: phy: realtek: add support for RTL8261C_CG
  net: phy: realtek: load firmware for RTL8261C

 drivers/net/phy/phy-c45.c              |  54 ++++
 drivers/net/phy/realtek/realtek_main.c | 408 +++++++++++++++++++++++++
 include/linux/phy.h                    |   2 +
 include/uapi/linux/mdio.h              |   2 +
 4 files changed, 466 insertions(+)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset()
  2026-06-04  7:42 [PATCH net-next v2 0/4] Add support for RTL8261C_CG javen
@ 2026-06-04  7:42 ` javen
  2026-06-04  8:17   ` Nicolai Buchwitz
  2026-06-04 12:21   ` Andrew Lunn
  2026-06-04  7:42 ` [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 11+ messages in thread
From: javen @ 2026-06-04  7:42 UTC (permalink / raw)
  To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
  Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu

From: Javen Xu <javen_xu@realsil.com.cn>

Add a generic Clause 45 software reset helper. The helper sets the reset
bit in the PMA/PMD control register and waits until the bit is cleared by
hardware.

Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
 - no changes, new file
---
 drivers/net/phy/phy-c45.c | 22 ++++++++++++++++++++++
 include/linux/phy.h       |  1 +
 2 files changed, 23 insertions(+)

diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 126951741428..216a2cb9e3f0 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -330,6 +330,28 @@ int genphy_c45_an_disable_aneg(struct phy_device *phydev)
 }
 EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg);
 
+/**
+ * genphy_c45_soft_reset - software reset the PHY via Clause 45 PMA/PMD control register
+ * @phydev: target phy_device struct
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int genphy_c45_soft_reset(struct phy_device *phydev)
+{
+	int ret, val;
+
+	ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
+			       MDIO_CTRL1_RESET);
+	if (ret < 0)
+		return ret;
+
+	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PMAPMD,
+					 MDIO_CTRL1, val,
+					 !(val & MDIO_CTRL1_RESET),
+					 5000, 100000, true);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_soft_reset);
+
 /**
  * genphy_c45_restart_aneg - Enable and restart auto-negotiation
  * @phydev: target phy_device struct
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 199a7aaa341b..25a66320df56 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2309,6 +2309,7 @@ int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
 /* Clause 45 PHY */
 int genphy_c45_restart_aneg(struct phy_device *phydev);
 int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart);
+int genphy_c45_soft_reset(struct phy_device *phydev);
 int genphy_c45_aneg_done(struct phy_device *phydev);
 int genphy_c45_read_link(struct phy_device *phydev);
 int genphy_c45_read_lpa(struct phy_device *phydev);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave()
  2026-06-04  7:42 [PATCH net-next v2 0/4] Add support for RTL8261C_CG javen
  2026-06-04  7:42 ` [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
@ 2026-06-04  7:42 ` javen
  2026-06-04  8:49   ` Nicolai Buchwitz
  2026-06-04  7:42 ` [PATCH net-next v2 3/4] net: phy: realtek: add support for RTL8261C_CG javen
  2026-06-04  7:42 ` [PATCH net-next v2 4/4] net: phy: realtek: load firmware " javen
  3 siblings, 1 reply; 11+ messages in thread
From: javen @ 2026-06-04  7:42 UTC (permalink / raw)
  To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
  Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu

From: Javen Xu <javen_xu@realsil.com.cn>

Add a generic helper to configure forced master/slave mode for Clause 45
PHYs using the 10GBASE-T AN control register.

Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
 - no changes, new file
---
 drivers/net/phy/phy-c45.c | 32 ++++++++++++++++++++++++++++++++
 include/linux/phy.h       |  1 +
 include/uapi/linux/mdio.h |  2 ++
 3 files changed, 35 insertions(+)

diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 216a2cb9e3f0..bbab5586c348 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -352,6 +352,38 @@ int genphy_c45_soft_reset(struct phy_device *phydev)
 }
 EXPORT_SYMBOL_GPL(genphy_c45_soft_reset);
 
+/**
+ * genphy_c45_config_master_slave - Configure Master/Slave setting for C45 PHYs
+ * @phydev: target phy_device struct
+ *
+ * Description: Configures the Master/Slave manual setting in the
+ * 10GBASE-T control register (MMD 7, Register 0x0020) according to
+ * IEEE 802.3 standards.
+ */
+int genphy_c45_config_master_slave(struct phy_device *phydev)
+{
+	u16 val = 0;
+
+	switch (phydev->master_slave_set) {
+	case MASTER_SLAVE_CFG_MASTER_FORCE:
+		val = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
+		break;
+	case MASTER_SLAVE_CFG_SLAVE_FORCE:
+		val = MDIO_AN_10GBT_CTRL_MS_ENABLE;
+		break;
+	case MASTER_SLAVE_CFG_UNKNOWN:
+	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+	default:
+		break;
+	}
+
+	return phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+				      MDIO_AN_10GBT_CTRL_MS_ENABLE |
+				      MDIO_AN_10GBT_CTRL_MS_VALUE, val);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_config_master_slave);
+
 /**
  * genphy_c45_restart_aneg - Enable and restart auto-negotiation
  * @phydev: target phy_device struct
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 25a66320df56..d9c8c86e2d81 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2310,6 +2310,7 @@ int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
 int genphy_c45_restart_aneg(struct phy_device *phydev);
 int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart);
 int genphy_c45_soft_reset(struct phy_device *phydev);
+int genphy_c45_config_master_slave(struct phy_device *phydev);
 int genphy_c45_aneg_done(struct phy_device *phydev);
 int genphy_c45_read_link(struct phy_device *phydev);
 int genphy_c45_read_lpa(struct phy_device *phydev);
diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h
index b2541c948fc1..2f36005b3133 100644
--- a/include/uapi/linux/mdio.h
+++ b/include/uapi/linux/mdio.h
@@ -332,6 +332,8 @@
 #define MDIO_AN_10GBT_CTRL_ADV2_5G	0x0080	/* Advertise 2.5GBASE-T */
 #define MDIO_AN_10GBT_CTRL_ADV5G	0x0100	/* Advertise 5GBASE-T */
 #define MDIO_AN_10GBT_CTRL_ADV10G	0x1000	/* Advertise 10GBASE-T */
+#define MDIO_AN_10GBT_CTRL_MS_ENABLE	0x8000	/* Master/slave manual config enable */
+#define MDIO_AN_10GBT_CTRL_MS_VALUE	0x4000	/* Master/slave config value (1=Master) */
 
 /* AN 10GBASE-T status register. */
 #define MDIO_AN_10GBT_STAT_LP2_5G	0x0020  /* LP is 2.5GBT capable */
-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH net-next v2 3/4] net: phy: realtek: add support for RTL8261C_CG
  2026-06-04  7:42 [PATCH net-next v2 0/4] Add support for RTL8261C_CG javen
  2026-06-04  7:42 ` [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
  2026-06-04  7:42 ` [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
@ 2026-06-04  7:42 ` javen
  2026-06-04  8:57   ` Nicolai Buchwitz
  2026-06-04  7:42 ` [PATCH net-next v2 4/4] net: phy: realtek: load firmware " javen
  3 siblings, 1 reply; 11+ messages in thread
From: javen @ 2026-06-04  7:42 UTC (permalink / raw)
  To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
  Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu

From: Javen Xu <javen_xu@realsil.com.cn>

This patch adds support for Realtek phy chip RTL8261C_CG. Its PHY ID is
0x001cc898.

Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
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


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH net-next v2 4/4] net: phy: realtek: load firmware for RTL8261C_CG
  2026-06-04  7:42 [PATCH net-next v2 0/4] Add support for RTL8261C_CG javen
                   ` (2 preceding siblings ...)
  2026-06-04  7:42 ` [PATCH net-next v2 3/4] net: phy: realtek: add support for RTL8261C_CG javen
@ 2026-06-04  7:42 ` javen
  3 siblings, 0 replies; 11+ messages in thread
From: javen @ 2026-06-04  7:42 UTC (permalink / raw)
  To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu
  Cc: netdev, linux-kernel, daniel, vladimir.oltean, Javen Xu

From: Javen Xu <javen_xu@realsil.com.cn>

This patch adds support for loading firmware. Download some parameters
for RTL8261C_CG.

Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
 - remove __pack, struct rtl8261x_fw_header and rtl8261x_fw_entry will not pad
 - reverse xmas tree for some definition
 - add explanation on rtl_phy_write_mmd_bits()
---
 drivers/net/phy/realtek/realtek_main.c | 230 +++++++++++++++++++++++++
 1 file changed, 230 insertions(+)

diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 86788feaae92..612ee7012e68 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -8,7 +8,9 @@
  * Copyright (c) 2004 Freescale Semiconductor, Inc.
  */
 #include <linux/bitops.h>
+#include <linux/crc32.h>
 #include <linux/ethtool_netlink.h>
+#include <linux/firmware.h>
 #include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/pm_wakeirq.h>
@@ -282,6 +284,42 @@
 					 RTL8261X_INT_ALDPS_CHG | \
 					 RTL8261X_INT_JABBER)
 
+#define FW_MAIN_MAGIC			0x52544C38
+#define FW_SUB_MAGIC_8261C		0x32363143
+#define RTL8261X_POLL_TIMEOUT_MS	100
+
+#define RTL8261C_CE_FW_NAME	"rtl_nic/rtl8261c.bin"
+MODULE_FIRMWARE(RTL8261C_CE_FW_NAME);
+
+enum rtl8261x_fw_op {
+	OP_WRITE = 0x00,	/* Write */
+	OP_POLL  = 0x02,	/* Polling */
+};
+
+struct rtl8261x_fw_header {
+	__le32 main_magic;	/* Main magic number 0x52544C38 ("RTL8") */
+	__le32 sub_magic;	/* Sub magic number */
+	__le16 version_major;	/* Major version */
+	__le16 version_minor;	/* Minor version */
+	__le16 num_entries;	/* Number of entries */
+	__le16 reserved;	/* Reserved */
+	__le32 crc32;		/* CRC32 checksum */
+};
+
+struct rtl8261x_fw_entry {
+	__u8  type;		/* Operation type (OP_*) */
+	__u8  dev;		/* MMD device */
+	__le16 addr;		/* Register address */
+	__u8  msb;		/* MSB bit position */
+	__u8  lsb;		/* LSB bit position */
+	__le16 value;		/* Value to write/compare */
+	__le16 timeout_ms;	/* Poll timeout in milliseconds */
+	__u8  poll_set;		/* Poll for set (1) or clear (0) */
+	__u8  reserved;		/* Reserved */
+};
+
+#define FW_HEADER_SIZE		sizeof(struct rtl8261x_fw_header)
+#define FW_ENTRY_SIZE		sizeof(struct rtl8261x_fw_entry)
 
 /* RTL8211E and RTL8211F support up to three LEDs */
 #define RTL8211x_LED_COUNT			3
@@ -303,6 +341,8 @@ struct rtl821x_priv {
 
 struct rtl8261x_priv {
 	u8 sub_phy_id;
+	const char *fw_name;
+	bool fw_loaded;
 };
 
 static int rtl821x_read_page(struct phy_device *phydev)
@@ -369,6 +409,7 @@ static int rtl8261x_probe(struct phy_device *phydev)
 
 	switch (sub_phy_id) {
 	case RTL8261C_CE_MODEL:
+		priv->fw_name = RTL8261C_CE_FW_NAME;
 		phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
 		break;
 
@@ -400,6 +441,178 @@ static int rtl8261x_get_features(struct phy_device *phydev)
 	return 0;
 }
 
+static int rtl8261x_verify_firmware(struct phy_device *phydev, const struct firmware *fw)
+{
+	const struct rtl8261x_fw_header *hdr;
+	u32 main_magic, sub_magic;
+	u32 calc_crc, file_crc;
+	size_t data_len;
+	u16 num_entries;
+
+	if (fw->size < FW_HEADER_SIZE) {
+		phydev_err(phydev, "Firmware too small: %zu bytes\n", fw->size);
+		return -EINVAL;
+	}
+
+	hdr = (const struct rtl8261x_fw_header *)fw->data;
+
+	main_magic = le32_to_cpu(hdr->main_magic);
+	if (main_magic != FW_MAIN_MAGIC) {
+		phydev_err(phydev, "Invalid firmware magic: 0x%08x\n", main_magic);
+		return -EINVAL;
+	}
+
+	sub_magic = le32_to_cpu(hdr->sub_magic);
+	if (sub_magic != FW_SUB_MAGIC_8261C) {
+		phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
+		return -EINVAL;
+	}
+
+	num_entries = le16_to_cpu(hdr->num_entries);
+	data_len = num_entries * FW_ENTRY_SIZE;
+
+	if (fw->size != sizeof(*hdr) + data_len) {
+		phydev_err(phydev, "Firmware size mismatch\n");
+		return -EINVAL;
+	}
+
+	calc_crc = crc32(~0, fw->data + FW_HEADER_SIZE, data_len) ^ ~0;
+	file_crc = le32_to_cpu(hdr->crc32);
+
+	if (calc_crc != file_crc) {
+		phydev_err(phydev, "CRC32 mismatch: calculated=0x%08x file=0x%08x\n",
+			   calc_crc, file_crc);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * rtl_phy_write_mmd_bits - Write a bitfield in an MMD register
+ * @phydev: PHY device structure
+ * @devnum: MMD device number
+ * @reg:    MMD register address
+ * @msb:    Most significant bit of the field (inclusive)
+ * @lsb:    Least significant bit of the field (inclusive)
+ * @val:    Value to write into the field (right-aligned)
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rtl_phy_write_mmd_bits(struct phy_device *phydev, int devnum,
+				  u16 reg, u8 msb, u8 lsb, u16 val)
+{
+	u32 reg_val;
+	int ret;
+
+	if (msb > 15 || lsb > msb)
+		return -EINVAL;
+
+	ret = phy_read_mmd(phydev, devnum, reg);
+	if (ret < 0)
+		return ret;
+	reg_val = ret;
+
+	reg_val &= ~GENMASK(msb, lsb);
+	reg_val |= (val << lsb) & GENMASK(msb, lsb);
+
+	return phy_write_mmd(phydev, devnum, reg, reg_val);
+}
+
+static int rtl8261x_fw_execute_entry(struct phy_device *phydev,
+				     const struct rtl8261x_fw_entry *entry)
+{
+	u16 addr, value, timeout_ms;
+	u8 dev, msb, lsb, poll_set;
+	u32 bits, expect_val;
+	int ret = 0;
+	int val;
+
+	dev = entry->dev;
+	addr = le16_to_cpu(entry->addr);
+	msb = entry->msb;
+	lsb = entry->lsb;
+	value = le16_to_cpu(entry->value);
+	timeout_ms = le16_to_cpu(entry->timeout_ms);
+	poll_set = entry->poll_set;
+
+	if (timeout_ms == 0)
+		timeout_ms = RTL8261X_POLL_TIMEOUT_MS;
+
+	switch (entry->type) {
+	case OP_WRITE:
+		ret = rtl_phy_write_mmd_bits(phydev, dev, addr, msb, lsb, value);
+		if (ret) {
+			phydev_err(phydev, "WRITE failed: dev=%d addr=0x%04x\n", dev, addr);
+			return ret;
+		}
+		break;
+
+	case OP_POLL: {
+		bits = GENMASK(msb, lsb);
+		expect_val = (value << lsb) & bits;
+
+		if (poll_set)
+			ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+							(val & bits) == expect_val,
+							1000, timeout_ms * 1000, false);
+		else
+			ret = phy_read_mmd_poll_timeout(phydev, dev, addr, val,
+							(val & bits) != expect_val,
+							1000, timeout_ms * 1000, false);
+		if (ret)
+			phydev_err(phydev, "POLL timeout: dev=%d addr=0x%04x\n",
+				   dev, addr);
+		break;
+	}
+	default:
+		phydev_err(phydev, "Unknown firmware operation: %d\n", entry->type);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int rtl8261x_fw_load(struct phy_device *phydev)
+{
+	struct rtl8261x_priv *priv = phydev->priv;
+	const struct rtl8261x_fw_entry *entry;
+	const struct rtl8261x_fw_header *hdr;
+	const struct firmware *fw;
+	int ret, i;
+
+	if (!priv->fw_name)
+		return 0;
+
+	ret = request_firmware(&fw, priv->fw_name, &phydev->mdio.dev);
+	if (ret) {
+		phydev_err(phydev, "Failed to load firmware %s: %d\n", priv->fw_name, ret);
+		return ret;
+	}
+
+	ret = rtl8261x_verify_firmware(phydev, fw);
+	if (ret)
+		goto release_fw;
+
+	hdr = (const struct rtl8261x_fw_header *)fw->data;
+
+	entry = (const struct rtl8261x_fw_entry *)(fw->data + FW_HEADER_SIZE);
+	for (i = 0; i < le16_to_cpu(hdr->num_entries); i++, entry++) {
+		ret = rtl8261x_fw_execute_entry(phydev, entry);
+		if (ret) {
+			phydev_err(phydev, "Entry %d failed: %d\n", i, ret);
+			goto release_fw;
+		}
+	}
+
+	priv->fw_loaded = true;
+
+release_fw:
+	release_firmware(fw);
+	return ret;
+}
+
 static int rtl8261x_config_intr(struct phy_device *phydev)
 {
 	int ret;
@@ -476,6 +689,22 @@ static int rtl8261x_config_aneg(struct phy_device *phydev)
 	return 0;
 }
 
+static int rtl8261x_config_init(struct phy_device *phydev)
+{
+	struct rtl8261x_priv *priv = phydev->priv;
+	int ret = 0;
+
+	if (priv->fw_name && !priv->fw_loaded) {
+		ret = rtl8261x_fw_load(phydev);
+		if (ret) {
+			phydev_err(phydev, "Firmware loading failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
 static int rtl821x_probe(struct phy_device *phydev)
 {
 	struct device *dev = &phydev->mdio.dev;
@@ -3171,6 +3400,7 @@ static struct phy_driver realtek_drvs[] = {
 		PHY_ID_MATCH_EXACT(RTL_8261C_CG),
 		.name			= "Realtek RTL8261C 10Gbps PHY",
 		.probe			= rtl8261x_probe,
+		.config_init		= rtl8261x_config_init,
 		.get_features		= rtl8261x_get_features,
 		.config_aneg		= rtl8261x_config_aneg,
 		.read_status		= genphy_c45_read_status,
-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset()
  2026-06-04  7:42 ` [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
@ 2026-06-04  8:17   ` Nicolai Buchwitz
  2026-06-04 12:21   ` Andrew Lunn
  1 sibling, 0 replies; 11+ messages in thread
From: Nicolai Buchwitz @ 2026-06-04  8:17 UTC (permalink / raw)
  To: javen
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean

Hi Javen

On 4.6.2026 09:42, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> Add a generic Clause 45 software reset helper. The helper sets the 
> reset
> bit in the PMA/PMD control register and waits until the bit is cleared 
> by
> hardware.
> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>

> [...]

> +
>  /**
>   * genphy_c45_restart_aneg - Enable and restart auto-negotiation
>   * @phydev: target phy_device struct
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 199a7aaa341b..25a66320df56 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -2309,6 +2309,7 @@ int genphy_c37_read_status(struct phy_device 
> *phydev, bool *changed);
>  /* Clause 45 PHY */
>  int genphy_c45_restart_aneg(struct phy_device *phydev);
>  int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool 
> restart);
> +int genphy_c45_soft_reset(struct phy_device *phydev);
>  int genphy_c45_aneg_done(struct phy_device *phydev);
>  int genphy_c45_read_link(struct phy_device *phydev);
>  int genphy_c45_read_lpa(struct phy_device *phydev);

nit: Ordering in c and header files mismatches, but this probably my OCD 
:-)

Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>

THanks,
Nicolai

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave()
  2026-06-04  7:42 ` [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
@ 2026-06-04  8:49   ` Nicolai Buchwitz
  2026-06-04 12:27     ` Andrew Lunn
  2026-06-05  3:19     ` Javen
  0 siblings, 2 replies; 11+ messages in thread
From: Nicolai Buchwitz @ 2026-06-04  8:49 UTC (permalink / raw)
  To: javen
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean

Hi Javen

On 4.6.2026 09:42, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> Add a generic helper to configure forced master/slave mode for Clause 
> 45
> PHYs using the 10GBASE-T AN control register.
> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>

> [...]

> +/**
> + * genphy_c45_config_master_slave - Configure Master/Slave setting for 
> C45 PHYs
> + * @phydev: target phy_device struct
> + *
> + * Description: Configures the Master/Slave manual setting in the
> + * 10GBASE-T control register (MMD 7, Register 0x0020) according to
> + * IEEE 802.3 standards.
> + */

Please document the return value (0 = unchanged, 1 = changed, <0 errno), 
like
it is done for genphy_c45_an_config_aneg().

> +int genphy_c45_config_master_slave(struct phy_device *phydev)
> +{
> +	u16 val = 0;
> +
> +	switch (phydev->master_slave_set) {
> +	case MASTER_SLAVE_CFG_MASTER_FORCE:
> +		val = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
> +		break;
> +	case MASTER_SLAVE_CFG_SLAVE_FORCE:
> +		val = MDIO_AN_10GBT_CTRL_MS_ENABLE;
> +		break;
> +	case MASTER_SLAVE_CFG_UNKNOWN:
> +	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> +	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> +	default:
> +		break;
> +	}

Per IEEE 802.3 (45.2.7.10.3), 7.32.13 (port type) is a copy of 9.10 /
CTL1000_PREFER_MASTER, like it is done in c22's 
genphy_setup_master_slave().

So set it for MASTER_PREFERRED, clear it for SLAVE_PREFERRED and leave 
UNKNOWN/UNSUPPORTED
untouched (return 0). Use -EOPNOTSUPP only for default.

> [...]

Thanks,
Nicolai

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 3/4] net: phy: realtek: add support for RTL8261C_CG
  2026-06-04  7:42 ` [PATCH net-next v2 3/4] net: phy: realtek: add support for RTL8261C_CG javen
@ 2026-06-04  8:57   ` Nicolai Buchwitz
  0 siblings, 0 replies; 11+ messages in thread
From: Nicolai Buchwitz @ 2026-06-04  8:57 UTC (permalink / raw)
  To: javen
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean

Hi Javen

On 4.6.2026 09:42, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> This patch adds support for Realtek phy chip RTL8261C_CG. Its PHY ID is
> 0x001cc898.
> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> 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

> [...]

> +
> +#define RTL8261C_CE_MODEL		0x00
> +#define RTL8261X_GBCR_REG		0xa412

IMHO this is just RTL822X_VND2_C22_REG(MII_CTRL1000) (= 0xa412), already
used elsewhere in the file. Drop the define and reuse the macro, as
Aleksander suggested on v1.

> [...]

> +struct rtl8261x_priv {
> +	u8 sub_phy_id;
> +};

sub_phy_id is only written, never read. Andrew already asked about the
unused priv fields on v1. A local in probe() is enough, drop the field?

> [...]

> +
> +	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);

See above and replace with RTL822X_VND2_C22_REG(MII_CTRL1000).

The aneg-restart handling looks correct now, thanks for picking up the 
v1
comment.

> [...]

Thanks,
Nicolai

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset()
  2026-06-04  7:42 ` [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
  2026-06-04  8:17   ` Nicolai Buchwitz
@ 2026-06-04 12:21   ` Andrew Lunn
  1 sibling, 0 replies; 11+ messages in thread
From: Andrew Lunn @ 2026-06-04 12:21 UTC (permalink / raw)
  To: javen
  Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu,
	netdev, linux-kernel, daniel, vladimir.oltean

On Thu, Jun 04, 2026 at 03:42:06PM +0800, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> Add a generic Clause 45 software reset helper. The helper sets the reset
> bit in the PMA/PMD control register and waits until the bit is cleared by
> hardware.
> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave()
  2026-06-04  8:49   ` Nicolai Buchwitz
@ 2026-06-04 12:27     ` Andrew Lunn
  2026-06-05  3:19     ` Javen
  1 sibling, 0 replies; 11+ messages in thread
From: Andrew Lunn @ 2026-06-04 12:27 UTC (permalink / raw)
  To: Nicolai Buchwitz
  Cc: javen, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean

> > +/**
> > + * genphy_c45_config_master_slave - Configure Master/Slave setting for
> > C45 PHYs
> > + * @phydev: target phy_device struct
> > + *
> > + * Description: Configures the Master/Slave manual setting in the
> > + * 10GBASE-T control register (MMD 7, Register 0x0020) according to
> > + * IEEE 802.3 standards.
> > + */
> 
> Please document the return value (0 = unchanged, 1 = changed, <0 errno),
> like
> it is done for genphy_c45_an_config_aneg().
> 
> > +int genphy_c45_config_master_slave(struct phy_device *phydev)

We already have genphy_c45_pma_baset1_setup_master_slave(). This new
function does the same thing, but not for T1 PHYs. It probably should
use a similar name.

> > +{
> > +	u16 val = 0;
> > +
> > +	switch (phydev->master_slave_set) {
> > +	case MASTER_SLAVE_CFG_MASTER_FORCE:
> > +		val = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
> > +		break;
> > +	case MASTER_SLAVE_CFG_SLAVE_FORCE:
> > +		val = MDIO_AN_10GBT_CTRL_MS_ENABLE;
> > +		break;
> > +	case MASTER_SLAVE_CFG_UNKNOWN:
> > +	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> > +	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> > +	default:
> > +		break;
> > +	}
> 
> Per IEEE 802.3 (45.2.7.10.3), 7.32.13 (port type) is a copy of 9.10 /
> CTL1000_PREFER_MASTER, like it is done in c22's genphy_setup_master_slave().
> 
> So set it for MASTER_PREFERRED, clear it for SLAVE_PREFERRED and leave
> UNKNOWN/UNSUPPORTED
> untouched (return 0). Use -EOPNOTSUPP only for default.

Agreed, making it the same as
genphy_c45_pma_baset1_setup_master_slave().

    Andrew

---
pw-bot: cr

^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave()
  2026-06-04  8:49   ` Nicolai Buchwitz
  2026-06-04 12:27     ` Andrew Lunn
@ 2026-06-05  3:19     ` Javen
  1 sibling, 0 replies; 11+ messages in thread
From: Javen @ 2026-06-05  3:19 UTC (permalink / raw)
  To: Nicolai Buchwitz
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	顾晓军,
	netdev, linux-kernel, daniel, vladimir.oltean

>Hi Javen
>
>On 4.6.2026 09:42, javen wrote:
>> From: Javen Xu <javen_xu@realsil.com.cn>
>>
>> Add a generic helper to configure forced master/slave mode for Clause
>> 45
>> PHYs using the 10GBASE-T AN control register.
>>
>> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
>
>> [...]
>
>> +/**
>> + * genphy_c45_config_master_slave - Configure Master/Slave setting
>> +for
>> C45 PHYs
>> + * @phydev: target phy_device struct
>> + *
>> + * Description: Configures the Master/Slave manual setting in the
>> + * 10GBASE-T control register (MMD 7, Register 0x0020) according to
>> + * IEEE 802.3 standards.
>> + */
>
>Please document the return value (0 = unchanged, 1 = changed, <0 errno), like
>it is done for genphy_c45_an_config_aneg().
>
>> +int genphy_c45_config_master_slave(struct phy_device *phydev) {
>> +     u16 val = 0;
>> +
>> +     switch (phydev->master_slave_set) {
>> +     case MASTER_SLAVE_CFG_MASTER_FORCE:
>> +             val = MDIO_AN_10GBT_CTRL_MS_ENABLE |
>MDIO_AN_10GBT_CTRL_MS_VALUE;
>> +             break;
>> +     case MASTER_SLAVE_CFG_SLAVE_FORCE:
>> +             val = MDIO_AN_10GBT_CTRL_MS_ENABLE;
>> +             break;
>> +     case MASTER_SLAVE_CFG_UNKNOWN:
>> +     case MASTER_SLAVE_CFG_MASTER_PREFERRED:
>> +     case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
>> +     default:
>> +             break;
>> +     }
>
>Per IEEE 802.3 (45.2.7.10.3), 7.32.13 (port type) is a copy of 9.10 /
>CTL1000_PREFER_MASTER, like it is done in c22's
>genphy_setup_master_slave().
>
>So set it for MASTER_PREFERRED, clear it for SLAVE_PREFERRED and leave
>UNKNOWN/UNSUPPORTED untouched (return 0). Use -EOPNOTSUPP only for
>default.

Thanks for your review.
I have applied all the suggestions to patch v3.

Thanks,
BRs,
Javen

>
>> [...]
>
>Thanks,
>Nicolai

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-06-05  3:21 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-04  7:42 [PATCH net-next v2 0/4] Add support for RTL8261C_CG javen
2026-06-04  7:42 ` [PATCH net-next v2 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
2026-06-04  8:17   ` Nicolai Buchwitz
2026-06-04 12:21   ` Andrew Lunn
2026-06-04  7:42 ` [PATCH net-next v2 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
2026-06-04  8:49   ` Nicolai Buchwitz
2026-06-04 12:27     ` Andrew Lunn
2026-06-05  3:19     ` Javen
2026-06-04  7:42 ` [PATCH net-next v2 3/4] net: phy: realtek: add support for RTL8261C_CG javen
2026-06-04  8:57   ` Nicolai Buchwitz
2026-06-04  7:42 ` [PATCH net-next v2 4/4] net: phy: realtek: load firmware " javen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox