* [PATCH net-next v5 0/4] Add support for RTL8261C_CG
@ 2026-06-11 3:18 javen
2026-06-11 3:18 ` [PATCH net-next v5 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: javen @ 2026-06-11 3:18 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu, nb
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: c45: add genphy_c45_soft_reset()
net: phy: c45: add genphy_c45_config_master_slave()
net: phy: realtek: add support for RTL8261C_CG
net: phy: realtek: load firmware for RTL8261C_CG
drivers/net/phy/phy-c45.c | 69 +++++
drivers/net/phy/realtek/realtek_main.c | 396 +++++++++++++++++++++++++
include/linux/phy.h | 1 +
include/uapi/linux/mdio.h | 3 +
4 files changed, 469 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v5 1/4] net: phy: c45: add genphy_c45_soft_reset()
2026-06-11 3:18 [PATCH net-next v5 0/4] Add support for RTL8261C_CG javen
@ 2026-06-11 3:18 ` javen
2026-06-11 3:18 ` [PATCH net-next v5 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: javen @ 2026-06-11 3:18 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu, nb
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.
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
Changes in v2:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
Changes in v4:
- no changes
Changes in v5:
- no changes
---
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..d8a83179ec40 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -384,6 +384,28 @@ int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart)
}
EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_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_aneg_done - return auto-negotiation complete status
* @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] 8+ messages in thread
* [PATCH net-next v5 2/4] net: phy: c45: add genphy_c45_config_master_slave()
2026-06-11 3:18 [PATCH net-next v5 0/4] Add support for RTL8261C_CG javen
2026-06-11 3:18 ` [PATCH net-next v5 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
@ 2026-06-11 3:18 ` javen
2026-06-13 23:59 ` Jakub Kicinski
2026-06-11 3:18 ` [PATCH net-next v5 3/4] net: phy: realtek: add support for RTL8261C_CG javen
2026-06-11 3:18 ` [PATCH net-next v5 4/4] net: phy: realtek: load firmware " javen
3 siblings, 1 reply; 8+ messages in thread
From: javen @ 2026-06-11 3:18 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu, nb
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
Changes in v3:
- re-order function according to the order in phy-c45.c
- add kernel-doc about return value
- add MASTER_SLAVE_CFG_MASTER_PREFERRED,
MASTER_SLAVE_CFG_SLAVE_PREFERRED, MASTER_SLAVE_CFG_UNKNOWN,
MASTER_SLAVE_CFG_UNSUPPORTED, MASTER_SLAVE_CFG_SLAVE_PREFERRED cfg
Changes in v4:
- no changes
Changes in v5:
- move genphy_c45_an_setup_master_slave() to genphy_c45_config_aneg(),
as that C22 does.
---
drivers/net/phy/phy-c45.c | 47 +++++++++++++++++++++++++++++++++++++++
include/uapi/linux/mdio.h | 3 +++
2 files changed, 50 insertions(+)
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index d8a83179ec40..c7ff46715f03 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -406,6 +406,47 @@ int genphy_c45_soft_reset(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(genphy_c45_soft_reset);
+/**
+ * genphy_c45_an_setup_master_slave - Configure Master/Slave setting for C45 PHYs
+ * @phydev: target phy_device struct
+ *
+ * Description: Configure the forced or preferred Master/Slave role
+ * 10GBASE-T control register (MMD 7, Register 0x0020) according to
+ * IEEE 802.3 standards.
+ *
+ * Returns negative errno code on failure, 0 if Master/Slave didn't change,
+ * or 1 if Master/Slave modes changed.
+ */
+static int genphy_c45_an_setup_master_slave(struct phy_device *phydev)
+{
+ u16 ctl = 0;
+
+ switch (phydev->master_slave_set) {
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ ctl = MDIO_AN_10GBT_CTRL_MS_PORT_TYPE;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ break;
+ case MASTER_SLAVE_CFG_MASTER_FORCE:
+ ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE | MDIO_AN_10GBT_CTRL_MS_VALUE;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ ctl = MDIO_AN_10GBT_CTRL_MS_ENABLE;
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_UNSUPPORTED:
+ return 0;
+ default:
+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ 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 |
+ MDIO_AN_10GBT_CTRL_MS_PORT_TYPE, ctl);
+}
+
/**
* genphy_c45_aneg_done - return auto-negotiation complete status
* @phydev: target phy_device struct
@@ -1247,6 +1288,12 @@ int genphy_c45_config_aneg(struct phy_device *phydev)
if (ret > 0)
changed = true;
+ ret = genphy_c45_an_setup_master_slave(phydev);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
EXPORT_SYMBOL_GPL(genphy_c45_config_aneg);
diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h
index b2541c948fc1..ffca03fd82c9 100644
--- a/include/uapi/linux/mdio.h
+++ b/include/uapi/linux/mdio.h
@@ -332,6 +332,9 @@
#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) */
+#define MDIO_AN_10GBT_CTRL_MS_PORT_TYPE 0x2000 /* Master Preferred Type */
/* 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] 8+ messages in thread
* [PATCH net-next v5 3/4] net: phy: realtek: add support for RTL8261C_CG
2026-06-11 3:18 [PATCH net-next v5 0/4] Add support for RTL8261C_CG javen
2026-06-11 3:18 ` [PATCH net-next v5 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
2026-06-11 3:18 ` [PATCH net-next v5 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
@ 2026-06-11 3:18 ` javen
2026-06-11 3:18 ` [PATCH net-next v5 4/4] net: phy: realtek: load firmware " javen
3 siblings, 0 replies; 8+ messages in thread
From: javen @ 2026-06-11 3:18 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu, nb
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:
- no changes, new file
Changes in v3:
- re-order function according to the order in phy-c45.c
- add kernel-doc about return value
- add MASTER_SLAVE_CFG_MASTER_PREFERRED,
MASTER_SLAVE_CFG_SLAVE_PREFERRED, MASTER_SLAVE_CFG_UNKNOWN,
MASTER_SLAVE_CFG_UNSUPPORTED, MASTER_SLAVE_CFG_SLAVE_PREFERRED cfg
Changes in v4:
- no changes
Changes in v5:
- remove genphy_c45_pma_setup_forced() for this is already done when
calling genphy_c45_config_aneg()
---
drivers/net/phy/realtek/realtek_main.c | 155 +++++++++++++++++++++++++
1 file changed, 155 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 27268811f564..623c4e1a6645 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,32 @@
#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_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
@@ -310,6 +340,119 @@ 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)
+{
+ int sub_phy_id, ret;
+
+ 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;
+
+ 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;
+ }
+
+ 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)
+{
+ u16 adv_1g = 0;
+ int 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 = ADVERTISE_1000FULL;
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_CTRL1000),
+ ADVERTISE_1000FULL, 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 +3144,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] 8+ messages in thread
* [PATCH net-next v5 4/4] net: phy: realtek: load firmware for RTL8261C_CG
2026-06-11 3:18 [PATCH net-next v5 0/4] Add support for RTL8261C_CG javen
` (2 preceding siblings ...)
2026-06-11 3:18 ` [PATCH net-next v5 3/4] net: phy: realtek: add support for RTL8261C_CG javen
@ 2026-06-11 3:18 ` javen
3 siblings, 0 replies; 8+ messages in thread
From: javen @ 2026-06-11 3:18 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu, nb
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.
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
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()
Changes in v3:
- add struct rtl8261x_priv
Changes in v4:
- add struct device *dev
Changes in v5:
- no changes
---
drivers/net/phy/realtek/realtek_main.c | 241 +++++++++++++++++++++++++
1 file changed, 241 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 623c4e1a6645..760ddd4ecc16 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>
@@ -281,6 +283,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
@@ -300,6 +338,11 @@ struct rtl821x_priv {
u16 iner;
};
+struct rtl8261x_priv {
+ const char *fw_name;
+ bool fw_loaded;
+};
+
static int rtl821x_read_page(struct phy_device *phydev)
{
return __phy_read(phydev, RTL821x_PAGE_SELECT);
@@ -342,8 +385,16 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
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;
+
+ phydev->priv = priv;
+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
RTL_8261X_SUB_PHY_ID_ADDR);
if (ret < 0)
@@ -357,6 +408,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;
@@ -387,6 +439,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;
@@ -453,6 +677,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;
@@ -3148,6 +3388,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] 8+ messages in thread
* Re: [PATCH net-next v5 2/4] net: phy: c45: add genphy_c45_config_master_slave()
2026-06-11 3:18 ` [PATCH net-next v5 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
@ 2026-06-13 23:59 ` Jakub Kicinski
0 siblings, 0 replies; 8+ messages in thread
From: Jakub Kicinski @ 2026-06-13 23:59 UTC (permalink / raw)
To: javen
Cc: andrew, hkallweit1, linux, davem, edumazet, pabeni, freddy_gu,
nb, netdev, linux-kernel, daniel, vladimir.oltean
On Thu, 11 Jun 2026 11:18:37 +0800 javen wrote:
> + * Returns negative errno code on failure, 0 if Master/Slave didn't change,
> + * or 1 if Master/Slave modes changed.
Returns:
the colon is needed otherwise:
Warning: drivers/net/phy/phy-c45.c:420 No description found for return value of 'genphy_c45_an_setup_master_slave'
There's also an AI review at
https://sashiko.dev/#/patchset/20260611031839.568-3-javen_xu@realsil.com.cn
--
pw-bot: cr
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v5 0/4] Add support for RTL8261C_CG
2026-06-15 9:08 [PATCH net-next v5 0/4] Add support " javen
@ 2026-06-16 2:07 ` Jakub Kicinski
0 siblings, 0 replies; 8+ messages in thread
From: Jakub Kicinski @ 2026-06-16 2:07 UTC (permalink / raw)
To: javen
Cc: andrew, hkallweit1, linux, davem, edumazet, pabeni, freddy_gu,
nb, netdev, linux-kernel, daniel, vladimir.oltean
On Mon, 15 Jun 2026 17:08:12 +0800 javen wrote:
> Add support for RTL8261C_CG and add support for loading firmware.
Looks like AI will have some comments on this series.
Not sure if all of them have already been discussed so let's revisit
this after the merge window.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v5 0/4] Add support for RTL8261C_CG
@ 2026-06-15 9:08 javen
2026-06-16 2:07 ` Jakub Kicinski
0 siblings, 1 reply; 8+ messages in thread
From: javen @ 2026-06-15 9:08 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, freddy_gu, nb
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: c45: add genphy_c45_soft_reset()
net: phy: c45: add genphy_c45_config_master_slave()
net: phy: realtek: add support for RTL8261C_CG
net: phy: realtek: load firmware for RTL8261C_CG
drivers/net/phy/phy-c45.c | 118 +++++++
drivers/net/phy/realtek/realtek_main.c | 407 +++++++++++++++++++++++++
include/linux/phy.h | 1 +
include/uapi/linux/mdio.h | 5 +
4 files changed, 531 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-16 2:07 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-11 3:18 [PATCH net-next v5 0/4] Add support for RTL8261C_CG javen
2026-06-11 3:18 ` [PATCH net-next v5 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
2026-06-11 3:18 ` [PATCH net-next v5 2/4] net: phy: c45: add genphy_c45_config_master_slave() javen
2026-06-13 23:59 ` Jakub Kicinski
2026-06-11 3:18 ` [PATCH net-next v5 3/4] net: phy: realtek: add support for RTL8261C_CG javen
2026-06-11 3:18 ` [PATCH net-next v5 4/4] net: phy: realtek: load firmware " javen
2026-06-15 9:08 [PATCH net-next v5 0/4] Add support " javen
2026-06-16 2:07 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox