mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next v1 0/2] Add support for RTL8261c
@ 2026-05-28  7:52 javen
  2026-05-28  7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
  2026-05-28  7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
  0 siblings, 2 replies; 13+ messages in thread
From: javen @ 2026-05-28  7:52 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 and add support for loading firmware.

Javen Xu (2):
  net: phy: realtek: add support for RTL8261
  net: phy: realtek: load firmware for RTL8261C

 drivers/net/phy/realtek/realtek_main.c | 536 +++++++++++++++++++++++++
 1 file changed, 536 insertions(+)

-- 
2.43.0


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

* [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
  2026-05-28  7:52 [PATCH net-next v1 0/2] Add support for RTL8261c javen
@ 2026-05-28  7:52 ` javen
  2026-05-28  9:37   ` Nicolai Buchwitz
                     ` (3 more replies)
  2026-05-28  7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
  1 sibling, 4 replies; 13+ messages in thread
From: javen @ 2026-05-28  7:52 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 RTL8261. Its PHY id is
0x001cc898 and 0x001cc899.

Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
 drivers/net/phy/realtek/realtek_main.c | 315 +++++++++++++++++++++++++
 1 file changed, 315 insertions(+)

diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 27268811f564..fe743fd0421b 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -22,6 +22,9 @@
 #include "../phylib.h"
 #include "realtek.h"
 
+#define RTL_8261C_CG			0x001cc898
+#define RTL_8261CE_CG			0x001cc899
+
 #define RTL8201F_IER_PAGE			0x07
 #define RTL8201F_IER				0x13
 #define RTL8201F_IER_LINK			BIT(13)
@@ -141,6 +144,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
@@ -252,6 +259,57 @@
 #define RTL_8251B				0x001cc862
 #define RTL_8261C				0x001cc890
 
+#define RTL8261C_CE_MODEL		0x00
+#define RTL8261D_MODEL			0x81
+#define RTL8261X_PHYSR_REG		0xa434
+#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_PHYSR_LINK		BIT(2)
+#define RTL8261X_PHYSR_DUPLEX		BIT(3)
+#define RTL8261X_PHYSR_SPEED_L		GENMASK(5, 4)
+#define RTL8261X_PHYSR_SPEED_H		GENMASK(10, 9)
+
+/* Concatenated 4-bit speed code values (SPD_H << 2 | SPD_L) */
+#define RTL8261X_SPEED_CODE_500M	0x3	/* H=0, L=3 */
+#define RTL8261X_SPEED_CODE_1000M	0x7	/* H=1, L=3 */
+#define RTL8261X_SPEED_CODE_2500M	0x8	/* H=2, L=0 */
+#define RTL8261X_SPEED_CODE_5000M	0x9	/* H=2, L=1 */
+#define RTL8261X_SPEED_500		500
+
+#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)
+
+#define RTL8261X_MULTIG_CTRL		0x0020
+#define RTL8261X_MASTER_SLAVE_MASK	GENMASK(15, 14)
+
+#define RTL8261X_MS_AUTO		0x0000
+#define RTL8261X_MS_SLAVE		0x8000
+#define RTL8261X_MS_MASTER		0xC000
+
+enum rtl8261x_chip_model {
+	RTL8261_MODEL_C_CE = 0,
+	RTL8261_MODEL_D,
+	RTL8261_MODEL_GENERIC,
+};
+
 /* RTL8211E and RTL8211F support up to three LEDs */
 #define RTL8211x_LED_COUNT			3
 
@@ -270,6 +328,12 @@ struct rtl821x_priv {
 	u16 iner;
 };
 
+struct rtl8261x_priv {
+	enum rtl8261x_chip_model model;
+	u8 sub_phy_id;
+	bool is_generic;
+};
+
 static int rtl821x_read_page(struct phy_device *phydev)
 {
 	return __phy_read(phydev, RTL821x_PAGE_SELECT);
@@ -310,6 +374,233 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
 	return phy_restore_page(phydev, oldpage, ret);
 }
 
+static int rtl8261x_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);
+}
+
+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;
+	priv->is_generic = false;
+
+	switch (sub_phy_id) {
+	case RTL8261C_CE_MODEL:
+		priv->model = RTL8261_MODEL_C_CE;
+		phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id);
+		break;
+
+	case RTL8261D_MODEL:
+		priv->model = RTL8261_MODEL_D;
+		phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id);
+		break;
+
+	default:
+		priv->model = RTL8261_MODEL_GENERIC;
+		priv->is_generic = true;
+		phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update driver for full support.\n",
+			    sub_phy_id);
+		break;
+	}
+	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_master_slave(struct phy_device *phydev)
+{
+	u16 val;
+	/*
+	 * Configure bits 15:14 of MMD 7.0x0020
+	 *
+	 * Bit 15 (Enable) | Bit 14 (Value) | Mode
+	 * ----------------|----------------|-------------
+	 *       0         |       0        | Auto (disabled)
+	 *       1         |       0        | Force Slave
+	 *       1         |       1        | Force Master
+	 */
+	switch (phydev->master_slave_set) {
+	case MASTER_SLAVE_CFG_MASTER_FORCE:
+		val = RTL8261X_MS_MASTER;
+		break;
+	case MASTER_SLAVE_CFG_SLAVE_FORCE:
+		val = RTL8261X_MS_SLAVE;
+		break;
+	case MASTER_SLAVE_CFG_UNKNOWN:
+	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+	default:
+		val = RTL8261X_MS_AUTO;
+		break;
+	}
+
+	return phy_modify_mmd(phydev, MDIO_MMD_AN, RTL8261X_MULTIG_CTRL,
+			      RTL8261X_MASTER_SLAVE_MASK, val);
+}
+
+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_read_status(struct phy_device *phydev)
+{
+	int ret, val, speed_code;
+
+	ret = genphy_c45_read_status(phydev);
+	if (ret < 0)
+		return ret;
+
+	val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_PHYSR_REG);
+	if (val < 0)
+		return val;
+
+	phydev->link = !!(val & RTL8261X_PHYSR_LINK);
+	if (!phydev->link) {
+		phydev->speed  = SPEED_UNKNOWN;
+		phydev->duplex = DUPLEX_UNKNOWN;
+		return 0;
+	}
+
+	phydev->duplex = (val & RTL8261X_PHYSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
+	speed_code = (FIELD_GET(RTL8261X_PHYSR_SPEED_H, val) << 2) |
+		      FIELD_GET(RTL8261X_PHYSR_SPEED_L, val);
+	switch (speed_code) {
+	case RTL8261X_SPEED_CODE_500M:
+		phydev->speed = RTL8261X_SPEED_500;
+		break;
+	case RTL8261X_SPEED_CODE_1000M:
+		phydev->speed = SPEED_1000;
+		break;
+	case RTL8261X_SPEED_CODE_2500M:
+		phydev->speed = SPEED_2500;
+		break;
+	case RTL8261X_SPEED_CODE_5000M:
+		phydev->speed = SPEED_5000;
+		break;
+	default:
+		phydev_warn(phydev, "unknown speed code 0x%x (PHYSR=0x%04x)\n", speed_code, val);
+		phydev->speed = SPEED_UNKNOWN;
+	break;
+	}
+
+	return 0;
+}
+
+static int rtl8261x_config_aneg(struct phy_device *phydev)
+{
+	u16 adv_1g = 0;
+	int ret;
+
+	if (phydev->autoneg == AUTONEG_DISABLE)
+		return genphy_c45_pma_setup_forced(phydev);
+
+	ret = rtl8261x_config_master_slave(phydev);
+	if (ret < 0)
+		return ret;
+
+	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 = BIT(9);
+
+	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
+				     RTL8261X_GBCR_REG,
+				     BIT(9), adv_1g);
+	if (ret < 0)
+		return ret;
+
+	if (ret > 0)
+		return genphy_c45_restart_aneg(phydev);
+
+	return 0;
+}
+
 static int rtl821x_probe(struct phy_device *phydev)
 {
 	struct device *dev = &phydev->mdio.dev;
@@ -3001,6 +3292,30 @@ 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_RTL8261D 10Gbps PHY",
+		.probe            = rtl8261x_probe,
+		.get_features     = rtl8261x_get_features,
+		.config_aneg      = rtl8261x_config_aneg,
+		.read_status      = rtl8261x_read_status,
+		.config_intr      = rtl8261x_config_intr,
+		.handle_interrupt = rtl8261x_handle_interrupt,
+		.soft_reset       = rtl8261x_soft_reset,
+		.suspend          = genphy_c45_pma_suspend,
+		.resume           = genphy_c45_pma_resume,
+	}, {
+		PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
+		.name             = "Realtek RTL8261CE 10Gbps PHY",
+		.probe            = rtl8261x_probe,
+		.get_features     = rtl8261x_get_features,
+		.config_aneg      = rtl8261x_config_aneg,
+		.read_status      = rtl8261x_read_status,
+		.config_intr      = rtl8261x_config_intr,
+		.handle_interrupt = rtl8261x_handle_interrupt,
+		.soft_reset       = rtl8261x_soft_reset,
+		.suspend          = genphy_c45_pma_suspend,
+		.resume           = genphy_c45_pma_resume,
 	},
 };
 
-- 
2.43.0


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

* [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
  2026-05-28  7:52 [PATCH net-next v1 0/2] Add support for RTL8261c javen
  2026-05-28  7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
@ 2026-05-28  7:52 ` javen
  2026-05-28  9:08   ` Nicolai Buchwitz
                     ` (2 more replies)
  1 sibling, 3 replies; 13+ messages in thread
From: javen @ 2026-05-28  7:52 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.

Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
 drivers/net/phy/realtek/realtek_main.c | 221 +++++++++++++++++++++++++
 1 file changed, 221 insertions(+)

diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index fe743fd0421b..d20cdc68cc62 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -18,12 +18,51 @@
 #include <linux/clk.h>
 #include <linux/string_choices.h>
 #include <net/phy/realtek_phy.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
 
 #include "../phylib.h"
 #include "realtek.h"
 
 #define RTL_8261C_CG			0x001cc898
 #define RTL_8261CE_CG			0x001cc899
+#define FW_MAIN_MAGIC			0x52544C38
+#define FW_SUB_MAGIC_8261C		0x32363143
+#define FW_SUB_MAGIC_8261D		0x32363144
+#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 */
+} __packed;
+
+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 */
+} __packed;
+
+#define FW_HEADER_SIZE		sizeof(struct rtl8261x_fw_header)
+#define FW_ENTRY_SIZE		sizeof(struct rtl8261x_fw_entry)
 
 #define RTL8201F_IER_PAGE			0x07
 #define RTL8201F_IER				0x13
@@ -332,6 +371,8 @@ struct rtl8261x_priv {
 	enum rtl8261x_chip_model model;
 	u8 sub_phy_id;
 	bool is_generic;
+	const char *fw_name;
+	bool fw_loaded;
 };
 
 static int rtl821x_read_page(struct phy_device *phydev)
@@ -413,16 +454,19 @@ static int rtl8261x_probe(struct phy_device *phydev)
 	switch (sub_phy_id) {
 	case RTL8261C_CE_MODEL:
 		priv->model = RTL8261_MODEL_C_CE;
+		priv->fw_name = RTL8261C_CE_FW_NAME;
 		phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id);
 		break;
 
 	case RTL8261D_MODEL:
 		priv->model = RTL8261_MODEL_D;
+		priv->fw_name = NULL;
 		phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id);
 		break;
 
 	default:
 		priv->model = RTL8261_MODEL_GENERIC;
+		priv->fw_name = NULL;
 		priv->is_generic = true;
 		phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update driver for full support.\n",
 			    sub_phy_id);
@@ -452,6 +496,165 @@ 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 calc_crc, file_crc;
+	size_t data_len;
+	u16 num_entries;
+	u32 main_magic, sub_magic;
+
+	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 && sub_magic != FW_SUB_MAGIC_8261D) {
+		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;
+}
+
+static int rtl_phy_write_mmd_bits(struct phy_device *phydev, int devnum,
+				  u16 reg, u8 msb, u8 lsb, u16 val)
+{
+	int ret;
+	u32 reg_val;
+
+	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)
+		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_master_slave(struct phy_device *phydev)
 {
 	u16 val;
@@ -601,6 +804,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->is_generic && !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;
@@ -3296,6 +3515,7 @@ static struct phy_driver realtek_drvs[] = {
 		PHY_ID_MATCH_EXACT(RTL_8261C_CG),
 		.name             = "Realtek RTL8261C_RTL8261D 10Gbps PHY",
 		.probe            = rtl8261x_probe,
+		.config_init	  = rtl8261x_config_init,
 		.get_features     = rtl8261x_get_features,
 		.config_aneg      = rtl8261x_config_aneg,
 		.read_status      = rtl8261x_read_status,
@@ -3308,6 +3528,7 @@ static struct phy_driver realtek_drvs[] = {
 		PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
 		.name             = "Realtek RTL8261CE 10Gbps PHY",
 		.probe            = rtl8261x_probe,
+		.config_init	  = rtl8261x_config_init,
 		.get_features     = rtl8261x_get_features,
 		.config_aneg      = rtl8261x_config_aneg,
 		.read_status      = rtl8261x_read_status,
-- 
2.43.0


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

* Re: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
  2026-05-28  7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
@ 2026-05-28  9:08   ` Nicolai Buchwitz
  2026-05-28 12:20   ` Daniel Golle
  2026-05-29  1:01   ` Andrew Lunn
  2 siblings, 0 replies; 13+ messages in thread
From: Nicolai Buchwitz @ 2026-05-28  9:08 UTC (permalink / raw)
  To: javen
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean

Hi Javen

On 28.5.2026 09:52, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> This patch adds support for loading firmware. Download some parameters
> for RTL8261C.

Maybe you can add a bit more context to the commit message? From what I
can read in the code this also add other variant etc.

> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
>  drivers/net/phy/realtek/realtek_main.c | 221 +++++++++++++++++++++++++
>  1 file changed, 221 insertions(+)
> 
> diff --git a/drivers/net/phy/realtek/realtek_main.c 
> b/drivers/net/phy/realtek/realtek_main.c
> index fe743fd0421b..d20cdc68cc62 100644
> --- a/drivers/net/phy/realtek/realtek_main.c
> +++ b/drivers/net/phy/realtek/realtek_main.c
> @@ -18,12 +18,51 @@
>  #include <linux/clk.h>
>  #include <linux/string_choices.h>
>  #include <net/phy/realtek_phy.h>
> +#include <linux/firmware.h>
> +#include <linux/crc32.h>

Move before net/ include

> 
>  #include "../phylib.h"
>  #include "realtek.h"
> 
>  #define RTL_8261C_CG			0x001cc898
>  #define RTL_8261CE_CG			0x001cc899
> +#define FW_MAIN_MAGIC			0x52544C38
> +#define FW_SUB_MAGIC_8261C		0x32363143
> +#define FW_SUB_MAGIC_8261D		0x32363144
> +#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 */
> +} __packed;
> +
> +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 */
> +} __packed;
> +
> +#define FW_HEADER_SIZE		sizeof(struct rtl8261x_fw_header)
> +#define FW_ENTRY_SIZE		sizeof(struct rtl8261x_fw_entry)
> 
>  #define RTL8201F_IER_PAGE			0x07
>  #define RTL8201F_IER				0x13
> @@ -332,6 +371,8 @@ struct rtl8261x_priv {
>  	enum rtl8261x_chip_model model;
>  	u8 sub_phy_id;
>  	bool is_generic;
> +	const char *fw_name;
> +	bool fw_loaded;
>  };
> 
>  static int rtl821x_read_page(struct phy_device *phydev)
> @@ -413,16 +454,19 @@ static int rtl8261x_probe(struct phy_device 
> *phydev)
>  	switch (sub_phy_id) {
>  	case RTL8261C_CE_MODEL:
>  		priv->model = RTL8261_MODEL_C_CE;
> +		priv->fw_name = RTL8261C_CE_FW_NAME;
>  		phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", 
> sub_phy_id);
>  		break;
> 
>  	case RTL8261D_MODEL:
>  		priv->model = RTL8261_MODEL_D;
> +		priv->fw_name = NULL;

priv->fw_name is already initialized with NULL by devm_kzalloc

>  		phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", 
> sub_phy_id);
>  		break;
> 
>  	default:
>  		priv->model = RTL8261_MODEL_GENERIC;
> +		priv->fw_name = NULL;

priv->fw_name is already initialized with NULL by devm_kzalloc

>  		priv->is_generic = true;
>  		phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. 
> Update driver for full support.\n",
>  			    sub_phy_id);
> @@ -452,6 +496,165 @@ 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 calc_crc, file_crc;
> +	size_t data_len;
> +	u16 num_entries;
> +	u32 main_magic, sub_magic;

Please re-order to match reverse x-mas

> +
> +	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 && sub_magic != 
> FW_SUB_MAGIC_8261D) {
> +		phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
> +		return -EINVAL;
> +	}

It checks for FW_SUB_MAGIC_8261D, but priv->fw_name is always NULL and 
no firmwaer?
Is this a prepartion for a follow-up or am I missing something?

> +
> +	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;
> +}
> +
> +static int rtl_phy_write_mmd_bits(struct phy_device *phydev, int 
> devnum,
> +				  u16 reg, u8 msb, u8 lsb, u16 val)
> +{
> +	int ret;
> +	u32 reg_val;

Please re-order to match reverse x-mas

> +
> +	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)
> +		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_master_slave(struct phy_device *phydev)
>  {
>  	u16 val;
> @@ -601,6 +804,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->is_generic && !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;
> @@ -3296,6 +3515,7 @@ static struct phy_driver realtek_drvs[] = {
>  		PHY_ID_MATCH_EXACT(RTL_8261C_CG),
>  		.name             = "Realtek RTL8261C_RTL8261D 10Gbps PHY",
>  		.probe            = rtl8261x_probe,
> +		.config_init	  = rtl8261x_config_init,

Use spaces before = to keep alignment of the rest.

>  		.get_features     = rtl8261x_get_features,
>  		.config_aneg      = rtl8261x_config_aneg,
>  		.read_status      = rtl8261x_read_status,
> @@ -3308,6 +3528,7 @@ static struct phy_driver realtek_drvs[] = {
>  		PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
>  		.name             = "Realtek RTL8261CE 10Gbps PHY",
>  		.probe            = rtl8261x_probe,
> +		.config_init	  = rtl8261x_config_init,

Use spaces before = to keep alignment of the rest.

>  		.get_features     = rtl8261x_get_features,
>  		.config_aneg      = rtl8261x_config_aneg,
>  		.read_status      = rtl8261x_read_status,

Nicolai

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

* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
  2026-05-28  7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
@ 2026-05-28  9:37   ` Nicolai Buchwitz
  2026-05-29  0:51     ` Andrew Lunn
  2026-05-28 12:39   ` Andrew Lunn
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Nicolai Buchwitz @ 2026-05-28  9:37 UTC (permalink / raw)
  To: javen
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean

Hi Javen

On 28.5.2026 09:52, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> This patch adds support for Realtek phy chip RTL8261. Its PHY id is
> 0x001cc898 and 0x001cc899.
> 
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
>  drivers/net/phy/realtek/realtek_main.c | 315 +++++++++++++++++++++++++
>  1 file changed, 315 insertions(+)
> 
> diff --git a/drivers/net/phy/realtek/realtek_main.c 
> b/drivers/net/phy/realtek/realtek_main.c
> index 27268811f564..fe743fd0421b 100644
> --- a/drivers/net/phy/realtek/realtek_main.c
> +++ b/drivers/net/phy/realtek/realtek_main.c
> @@ -22,6 +22,9 @@
>  #include "../phylib.h"
>  #include "realtek.h"
> 
> +#define RTL_8261C_CG			0x001cc898
> +#define RTL_8261CE_CG			0x001cc899

Imho these should be added below RTL_8261C and not at the top.

> +
>  #define RTL8201F_IER_PAGE			0x07
>  #define RTL8201F_IER				0x13
>  #define RTL8201F_IER_LINK			BIT(13)
> @@ -141,6 +144,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
> @@ -252,6 +259,57 @@
>  #define RTL_8251B				0x001cc862
>  #define RTL_8261C				0x001cc890
> 
> +#define RTL8261C_CE_MODEL		0x00
> +#define RTL8261D_MODEL			0x81
> +#define RTL8261X_PHYSR_REG		0xa434
> +#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_PHYSR_LINK		BIT(2)
> +#define RTL8261X_PHYSR_DUPLEX		BIT(3)
> +#define RTL8261X_PHYSR_SPEED_L		GENMASK(5, 4)
> +#define RTL8261X_PHYSR_SPEED_H		GENMASK(10, 9)
> +
> +/* Concatenated 4-bit speed code values (SPD_H << 2 | SPD_L) */
> +#define RTL8261X_SPEED_CODE_500M	0x3	/* H=0, L=3 */
> +#define RTL8261X_SPEED_CODE_1000M	0x7	/* H=1, L=3 */
> +#define RTL8261X_SPEED_CODE_2500M	0x8	/* H=2, L=0 */
> +#define RTL8261X_SPEED_CODE_5000M	0x9	/* H=2, L=1 */
> +#define RTL8261X_SPEED_500		500

500 MBit/s isnt a standard rate which also isnt represented in ethtool.
Is this just for documentation or intended to be used? If yes, how?

> [...]

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

Feature bits for 2.5 and 5 GBit/s are set unconditionally - even for the 
generic variant.
Do all variants have these capabilities? If not better add check for at 
least the generic one.

> +
> +	return 0;
> +}
> +
> +static int rtl8261x_config_master_slave(struct phy_device *phydev)
> +{
> +	u16 val;
> +	/*
> +	 * Configure bits 15:14 of MMD 7.0x0020
> +	 *
> +	 * Bit 15 (Enable) | Bit 14 (Value) | Mode
> +	 * ----------------|----------------|-------------
> +	 *       0         |       0        | Auto (disabled)
> +	 *       1         |       0        | Force Slave
> +	 *       1         |       1        | Force Master
> +	 */
> +	switch (phydev->master_slave_set) {
> +	case MASTER_SLAVE_CFG_MASTER_FORCE:
> +		val = RTL8261X_MS_MASTER;
> +		break;
> +	case MASTER_SLAVE_CFG_SLAVE_FORCE:
> +		val = RTL8261X_MS_SLAVE;
> +		break;
> +	case MASTER_SLAVE_CFG_UNKNOWN:
> +	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> +	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> +	default:
> +		val = RTL8261X_MS_AUTO;
> +		break;
> +	}
> +
> +	return phy_modify_mmd(phydev, MDIO_MMD_AN, RTL8261X_MULTIG_CTRL,
> +			      RTL8261X_MASTER_SLAVE_MASK, val);
> +}

RTL8261X_MULTIG_CTRL is a IEEE-defined register, so it can use 
MDIO_AN_10GBT_CTRL instead.
Also the define for RTL8261X_MULTIG_CTRL can be dropped.

While at it I would suggest to add MDIO_AN_10GBT_CTRL_MS_ENABLE (bit 15) 
and
MDIO_AN_10GBT_CTRL_MS_VALUE (bit 14) to include/uapi/linux/mdio.h and 
also make use of it.
RTL8261X_MS_AUTO would become

   val = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;

> [...]

> +
> +static int rtl8261x_read_status(struct phy_device *phydev)
> +{
> +	int ret, val, speed_code;
> +
> +	ret = genphy_c45_read_status(phydev);
> +	if (ret < 0)
> +		return ret;
> +
> +	val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_PHYSR_REG);
> +	if (val < 0)
> +		return val;
> +
> +	phydev->link = !!(val & RTL8261X_PHYSR_LINK);
> +	if (!phydev->link) {
> +		phydev->speed  = SPEED_UNKNOWN;
> +		phydev->duplex = DUPLEX_UNKNOWN;
> +		return 0;
> +	}

Why does it need to overreide the values from genphy_c45_read_status() ?

> +
> +	phydev->duplex = (val & RTL8261X_PHYSR_DUPLEX) ? DUPLEX_FULL : 
> DUPLEX_HALF;
> +	speed_code = (FIELD_GET(RTL8261X_PHYSR_SPEED_H, val) << 2) |
> +		      FIELD_GET(RTL8261X_PHYSR_SPEED_L, val);
> +	switch (speed_code) {
> +	case RTL8261X_SPEED_CODE_500M:
> +		phydev->speed = RTL8261X_SPEED_500;

See question above (defines).

> +		break;
> +	case RTL8261X_SPEED_CODE_1000M:
> +		phydev->speed = SPEED_1000;
> +		break;
> +	case RTL8261X_SPEED_CODE_2500M:
> +		phydev->speed = SPEED_2500;
> +		break;
> +	case RTL8261X_SPEED_CODE_5000M:
> +		phydev->speed = SPEED_5000;
> +		break;
> +	default:
> +		phydev_warn(phydev, "unknown speed code 0x%x (PHYSR=0x%04x)\n", 
> speed_code, val);
> +		phydev->speed = SPEED_UNKNOWN;
> +	break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rtl8261x_config_aneg(struct phy_device *phydev)
> +{
> +	u16 adv_1g = 0;
> +	int ret;
> +
> +	if (phydev->autoneg == AUTONEG_DISABLE)
> +		return genphy_c45_pma_setup_forced(phydev);
> +
> +	ret = rtl8261x_config_master_slave(phydev);
> +	if (ret < 0)
> +		return ret;
> +
> +	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 = BIT(9);
> +
> +	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
> +				     RTL8261X_GBCR_REG,
> +				     BIT(9), adv_1g);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ret > 0)
> +		return genphy_c45_restart_aneg(phydev);

This does not take rtl8261x_config_master_slave() into account. So maybe 
change rtl8261x_config_master_slave

    return phy_modify_mmd_changed ...

and keep the result in a bool and also trigger auto nego restart if 
needed.

> +
> +	return 0;
> +}



> +
>  static int rtl821x_probe(struct phy_device *phydev)
>  {
>  	struct device *dev = &phydev->mdio.dev;
> @@ -3001,6 +3292,30 @@ 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_RTL8261D 10Gbps PHY",
> +		.probe            = rtl8261x_probe,

RTL8261C/RTL8261D? Or just the c variant as the phy id suggests?

> +		.get_features     = rtl8261x_get_features,
> +		.config_aneg      = rtl8261x_config_aneg,
> +		.read_status      = rtl8261x_read_status,
> +		.config_intr      = rtl8261x_config_intr,
> +		.handle_interrupt = rtl8261x_handle_interrupt,
> +		.soft_reset       = rtl8261x_soft_reset,
> +		.suspend          = genphy_c45_pma_suspend,
> +		.resume           = genphy_c45_pma_resume,
> +	}, {
> +		PHY_ID_MATCH_EXACT(RTL_8261CE_CG),
> +		.name             = "Realtek RTL8261CE 10Gbps PHY",
> +		.probe            = rtl8261x_probe,
> +		.get_features     = rtl8261x_get_features,
> +		.config_aneg      = rtl8261x_config_aneg,
> +		.read_status      = rtl8261x_read_status,
> +		.config_intr      = rtl8261x_config_intr,
> +		.handle_interrupt = rtl8261x_handle_interrupt,
> +		.soft_reset       = rtl8261x_soft_reset,
> +		.suspend          = genphy_c45_pma_suspend,
> +		.resume           = genphy_c45_pma_resume,
>  	},
>  };

Please also make sure for both patches that checkpatch has no further 
complaints. Thanks.

Nicolai

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

* Re: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
  2026-05-28  7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
  2026-05-28  9:08   ` Nicolai Buchwitz
@ 2026-05-28 12:20   ` Daniel Golle
  2026-06-01  5:38     ` Javen
  2026-05-29  1:01   ` Andrew Lunn
  2 siblings, 1 reply; 13+ messages in thread
From: Daniel Golle @ 2026-05-28 12:20 UTC (permalink / raw)
  To: javen
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, vladimir.oltean,
	Balázs Triszka

On Thu, May 28, 2026 at 03:52:26PM +0800, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
> 
> This patch adds support for loading firmware. Download some parameters
> for RTL8261C.

I'd like to bring to your attention that the OpenWrt community
has impemented a more complete support for initial register patching
using a extremely similar firmware format.

https://github.com/openwrt/openwrt/blob/main/target/linux/generic/pending-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch

We used 'RTK_PATCH_OP_*' while this series uses only 'OP_*' as
macro names defining the initval patching operations.

As the vendor downstream driver had the operations hard-coded in tables
our community came up with a tool to generate the patching blobs from the
GPL-licensed vendor driver:

https://github.com/balika011/realtek_phy_firmware

Given the similarity I wonder if this code has been taken from Balázs
Triszka work (which clearly predates it) without giving him the due
credit.

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

* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
  2026-05-28  7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
  2026-05-28  9:37   ` Nicolai Buchwitz
@ 2026-05-28 12:39   ` Andrew Lunn
  2026-05-28 12:42   ` Andrew Lunn
  2026-05-28 16:56   ` Aleksander Jan Bajkowski
  3 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-05-28 12:39 UTC (permalink / raw)
  To: javen
  Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu,
	netdev, linux-kernel, daniel, vladimir.oltean

> +static int rtl8261x_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);

We have a few copies of this function in various PHY drivers. Please
add a genphy_c45_soft_reset() helper.

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

blank line

> +	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;
> +	priv->is_generic = false;
> +
> +	switch (sub_phy_id) {
> +	case RTL8261C_CE_MODEL:
> +		priv->model = RTL8261_MODEL_C_CE;
> +		phydev_info(phydev, "RTL8261C/CE detected (sub_id 0x%02x)\n", sub_phy_id);
> +		break;
> +
> +	case RTL8261D_MODEL:
> +		priv->model = RTL8261_MODEL_D;
> +		phydev_info(phydev, "RTL8261D detected (sub_id 0x%02x)\n", sub_phy_id);
> +		break;
> +
> +	default:
> +		priv->model = RTL8261_MODEL_GENERIC;
> +		priv->is_generic = true;
> +		phydev_warn(phydev, "Unknown sub_id 0x%02x! Using GENERIC mode. Update driver for full support.\n",
> +			    sub_phy_id);
> +		break;

This will get ignored. It is better to return -ENODEV so the probe
fails.

	Andrew

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

* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
  2026-05-28  7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
  2026-05-28  9:37   ` Nicolai Buchwitz
  2026-05-28 12:39   ` Andrew Lunn
@ 2026-05-28 12:42   ` Andrew Lunn
  2026-05-28 16:56   ` Aleksander Jan Bajkowski
  3 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-05-28 12:42 UTC (permalink / raw)
  To: javen
  Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu,
	netdev, linux-kernel, daniel, vladimir.oltean

> +struct rtl8261x_priv {
> +	enum rtl8261x_chip_model model;
> +	u8 sub_phy_id;
> +	bool is_generic;

These don't appear to be used anywhere, just set. Are they really
needed?

	Andrew

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

* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
  2026-05-28  7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
                     ` (2 preceding siblings ...)
  2026-05-28 12:42   ` Andrew Lunn
@ 2026-05-28 16:56   ` Aleksander Jan Bajkowski
  3 siblings, 0 replies; 13+ messages in thread
From: Aleksander Jan Bajkowski @ 2026-05-28 16:56 UTC (permalink / raw)
  To: javen, andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu
  Cc: netdev, linux-kernel, daniel, vladimir.oltean

Hi Javen,

On 28/05/2026 09:52, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch adds support for Realtek phy chip RTL8261. Its PHY id is
> 0x001cc898 and 0x001cc899.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
>   drivers/net/phy/realtek/realtek_main.c | 315 +++++++++++++++++++++++++
>   1 file changed, 315 insertions(+)
>
> diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
> index 27268811f564..fe743fd0421b 100644
> --- a/drivers/net/phy/realtek/realtek_main.c
> +++ b/drivers/net/phy/realtek/realtek_main.c
> +static int rtl8261x_config_aneg(struct phy_device *phydev)
> +{
> +	u16 adv_1g = 0;
> +	int ret;
> +
> +	if (phydev->autoneg == AUTONEG_DISABLE)
> +		return genphy_c45_pma_setup_forced(phydev);
> +
> +	ret = rtl8261x_config_master_slave(phydev);
> +	if (ret < 0)
> +		return ret;
> +
> +	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 = BIT(9);

		adv_1g = ADVERTISE_1000FULL;

> +
> +	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
> +				     RTL8261X_GBCR_REG,
> +				     BIT(9), adv_1g);

This is the C22 register mapped in VEND2 page. You can replace it with:

	ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
				     RTL822X_VND2_C22_REG(MII_CTRL1000 ),
				     ADVERTISE_1000FULL, adv_1g);


Best regards,
Aleksander


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

* Re: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
  2026-05-28  9:37   ` Nicolai Buchwitz
@ 2026-05-29  0:51     ` Andrew Lunn
  2026-06-03  2:35       ` Javen
  0 siblings, 1 reply; 13+ messages in thread
From: Andrew Lunn @ 2026-05-29  0:51 UTC (permalink / raw)
  To: Nicolai Buchwitz
  Cc: javen, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean

> > +static int rtl8261x_config_master_slave(struct phy_device *phydev)
> > +{
> > +	u16 val;
> > +	/*
> > +	 * Configure bits 15:14 of MMD 7.0x0020
> > +	 *
> > +	 * Bit 15 (Enable) | Bit 14 (Value) | Mode
> > +	 * ----------------|----------------|-------------
> > +	 *       0         |       0        | Auto (disabled)
> > +	 *       1         |       0        | Force Slave
> > +	 *       1         |       1        | Force Master
> > +	 */
> > +	switch (phydev->master_slave_set) {
> > +	case MASTER_SLAVE_CFG_MASTER_FORCE:
> > +		val = RTL8261X_MS_MASTER;
> > +		break;
> > +	case MASTER_SLAVE_CFG_SLAVE_FORCE:
> > +		val = RTL8261X_MS_SLAVE;
> > +		break;
> > +	case MASTER_SLAVE_CFG_UNKNOWN:
> > +	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> > +	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> > +	default:
> > +		val = RTL8261X_MS_AUTO;
> > +		break;
> > +	}
> > +
> > +	return phy_modify_mmd(phydev, MDIO_MMD_AN, RTL8261X_MULTIG_CTRL,
> > +			      RTL8261X_MASTER_SLAVE_MASK, val);
> > +}
> 
> RTL8261X_MULTIG_CTRL is a IEEE-defined register, so it can use
> MDIO_AN_10GBT_CTRL instead.
> Also the define for RTL8261X_MULTIG_CTRL can be dropped.
> 
> While at it I would suggest to add MDIO_AN_10GBT_CTRL_MS_ENABLE (bit 15) and
> MDIO_AN_10GBT_CTRL_MS_VALUE (bit 14) to include/uapi/linux/mdio.h and also
> make use of it.
> RTL8261X_MS_AUTO would become

If this is all just standard 802.3, the function should be added to
phy-c45.c, so it can be used by any PHY driver.

	Andrew

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

* Re: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
  2026-05-28  7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
  2026-05-28  9:08   ` Nicolai Buchwitz
  2026-05-28 12:20   ` Daniel Golle
@ 2026-05-29  1:01   ` Andrew Lunn
  2 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-05-29  1:01 UTC (permalink / raw)
  To: javen
  Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu,
	netdev, linux-kernel, daniel, vladimir.oltean

> +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 */
> +} __packed;
> +
> +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 */
> +} __packed;

It looks like __packed should not be needed in both of
these. Everything is aligned so the compiler will naturally pack
them.

> +	sub_magic = le32_to_cpu(hdr->sub_magic);
> +	if (sub_magic != FW_SUB_MAGIC_8261C && sub_magic != FW_SUB_MAGIC_8261D) {
> +		phydev_err(phydev, "Invalid sub magic: 0x%08x\n", sub_magic);
> +		return -EINVAL;
> +	}

Should there be validation you are not trying to install C firmware on
D hardware? And visa versa?

> +static int rtl_phy_write_mmd_bits(struct phy_device *phydev, int devnum,
> +				  u16 reg, u8 msb, u8 lsb, u16 val)
> +{
> +	int ret;
> +	u32 reg_val;
> +
> +	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);

The logic here is not very obvious. Please could you add some kdoc to
explain what this function does.

	Andrew

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

* RE: [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C
  2026-05-28 12:20   ` Daniel Golle
@ 2026-06-01  5:38     ` Javen
  0 siblings, 0 replies; 13+ messages in thread
From: Javen @ 2026-06-01  5:38 UTC (permalink / raw)
  To: Daniel Golle
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	顾晓军,
	netdev, linux-kernel, vladimir.oltean, Balázs Triszka

>On Thu, May 28, 2026 at 03:52:26PM +0800, javen wrote:
>> From: Javen Xu <javen_xu@realsil.com.cn>
>>
>> This patch adds support for loading firmware. Download some parameters
>> for RTL8261C.
>
>I'd like to bring to your attention that the OpenWrt community has
>impemented a more complete support for initial register patching using a
>extremely similar firmware format.
>
>https://github.com/openwrt/openwrt/blob/main/target/linux/generic/pendi
>ng-6.18/742-net-phy-realtek-add-5G-and-10G-PHY-support.patch
>
>We used 'RTK_PATCH_OP_*' while this series uses only 'OP_*' as macro
>names defining the initval patching operations.
>
>As the vendor downstream driver had the operations hard-coded in tables
>our community came up with a tool to generate the patching blobs from the
>GPL-licensed vendor driver:
>
>https://github.com/balika011/realtek_phy_firmware
>
>Given the similarity I wonder if this code has been taken from Balázs Triszka
>work (which clearly predates it) without giving him the due credit.

Thank you for bringing this to my attention and sharing the links to the OpenWrt community's and Balázs Triszka's work.

I have reviewed the repository and the patch you mentioned. I want to clarify that our code was developed independently. Since both implementations are tackling the exact same hardware requirements for Realtek PHYs, the fundamental hardware operations―such as read, write, mask, and delay―are strictly defined by the hardware. In this patch, we only kept some of the operations that are used, such as op_poll.

I hope this clears up the misunderstanding. 

BRs,
Javen

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

* RE: [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261
  2026-05-29  0:51     ` Andrew Lunn
@ 2026-06-03  2:35       ` Javen
  0 siblings, 0 replies; 13+ messages in thread
From: Javen @ 2026-06-03  2:35 UTC (permalink / raw)
  To: Andrew Lunn, Nicolai Buchwitz
  Cc: hkallweit1, linux, davem, edumazet, kuba, pabeni,
	顾晓军,
	netdev, linux-kernel, daniel, vladimir.oltean

>
>> > +static int rtl8261x_config_master_slave(struct phy_device *phydev)
>> > +{
>> > +   u16 val;
>> > +   /*
>> > +    * Configure bits 15:14 of MMD 7.0x0020
>> > +    *
>> > +    * Bit 15 (Enable) | Bit 14 (Value) | Mode
>> > +    * ----------------|----------------|-------------
>> > +    *       0         |       0        | Auto (disabled)
>> > +    *       1         |       0        | Force Slave
>> > +    *       1         |       1        | Force Master
>> > +    */
>> > +   switch (phydev->master_slave_set) {
>> > +   case MASTER_SLAVE_CFG_MASTER_FORCE:
>> > +           val = RTL8261X_MS_MASTER;
>> > +           break;
>> > +   case MASTER_SLAVE_CFG_SLAVE_FORCE:
>> > +           val = RTL8261X_MS_SLAVE;
>> > +           break;
>> > +   case MASTER_SLAVE_CFG_UNKNOWN:
>> > +   case MASTER_SLAVE_CFG_MASTER_PREFERRED:
>> > +   case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
>> > +   default:
>> > +           val = RTL8261X_MS_AUTO;
>> > +           break;
>> > +   }
>> > +
>> > +   return phy_modify_mmd(phydev, MDIO_MMD_AN,
>RTL8261X_MULTIG_CTRL,
>> > +                         RTL8261X_MASTER_SLAVE_MASK, val); }
>>
>> RTL8261X_MULTIG_CTRL is a IEEE-defined register, so it can use
>> MDIO_AN_10GBT_CTRL instead.
>> Also the define for RTL8261X_MULTIG_CTRL can be dropped.
>>
>> While at it I would suggest to add MDIO_AN_10GBT_CTRL_MS_ENABLE (bit
>> 15) and MDIO_AN_10GBT_CTRL_MS_VALUE (bit 14) to
>> include/uapi/linux/mdio.h and also make use of it.
>> RTL8261X_MS_AUTO would become
>
>If this is all just standard 802.3, the function should be added to phy-c45.c, so
>it can be used by any PHY driver.
>
>        Andrew

Thanks for you suggestions. All the changes will be added into next version.

BRs,
Javen

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

end of thread, other threads:[~2026-06-03  2:35 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-28  7:52 [PATCH net-next v1 0/2] Add support for RTL8261c javen
2026-05-28  7:52 ` [PATCH net-next v1 1/2] net: phy: realtek: add support for RTL8261 javen
2026-05-28  9:37   ` Nicolai Buchwitz
2026-05-29  0:51     ` Andrew Lunn
2026-06-03  2:35       ` Javen
2026-05-28 12:39   ` Andrew Lunn
2026-05-28 12:42   ` Andrew Lunn
2026-05-28 16:56   ` Aleksander Jan Bajkowski
2026-05-28  7:52 ` [PATCH net-next v1 2/2] net: phy: realtek: load firmware for RTL8261C javen
2026-05-28  9:08   ` Nicolai Buchwitz
2026-05-28 12:20   ` Daniel Golle
2026-06-01  5:38     ` Javen
2026-05-29  1:01   ` Andrew Lunn

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

all inboxes | Powered by JetHome®