* [PATCH net-next v8 0/4] Add support for RTL8261C_CG
@ 2026-07-02 3:20 javen
2026-07-02 3:20 ` [PATCH net-next v8 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-07-02 3:20 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 setup and read master/slave helpers
net: phy: realtek: add support for RTL8261C_CG
net: phy: realtek: load firmware for RTL8261C_CG
drivers/net/phy/phy-c45.c | 125 ++++++++
drivers/net/phy/realtek/realtek_main.c | 400 +++++++++++++++++++++++++
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
* [PATCH net-next v8 1/4] net: phy: c45: add genphy_c45_soft_reset()
2026-07-02 3:20 [PATCH net-next v8 0/4] Add support for RTL8261C_CG javen
@ 2026-07-02 3:20 ` javen
2026-07-02 12:12 ` Maxime Chevallier
2026-07-02 3:20 ` [PATCH net-next v8 2/4] net: phy: c45: add setup and read master/slave helpers javen
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: javen @ 2026-07-02 3:20 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
Changes in v6:
- increase timeout to 600ms
Changes in v7:
- no changes
Changes in v8:
- 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..60d044156a83 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, 600000, 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 related [flat|nested] 8+ messages in thread
* [PATCH net-next v8 2/4] net: phy: c45: add setup and read master/slave helpers
2026-07-02 3:20 [PATCH net-next v8 0/4] Add support for RTL8261C_CG javen
2026-07-02 3:20 ` [PATCH net-next v8 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
@ 2026-07-02 3:20 ` javen
2026-07-02 3:20 ` [PATCH net-next v8 3/4] net: phy: realtek: add support for RTL8261C_CG javen
2026-07-02 3:20 ` [PATCH net-next v8 4/4] net: phy: realtek: load firmware " javen
3 siblings, 0 replies; 8+ messages in thread
From: javen @ 2026-07-02 3:20 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 two static helpers in drivers/net/phy/phy-c45.c to
configure and read back master-slave roles for non BASE-T1 Clause 45
PHYs via the 10GBASE-T AN control/status registers.
These helpers are wired into genphy_c45_config_aneg() and
genphy_c45_read_status(). This changes the observable ethtool output
for drivers using the generic c45 read path.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
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.
Changes in v6:
- add colon in the function description
- add genphy_c45_read_master_slave in read function
Changes in v7:
- when phydev->link is down, just return UNKNOWN
- modify commit message
Changes in v8:
- no changes
---
drivers/net/phy/phy-c45.c | 103 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/mdio.h | 5 ++
2 files changed, 108 insertions(+)
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 60d044156a83..df682d3ebd5a 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -406,6 +406,97 @@ 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.
+ *
+ * Return: 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_read_master_slave - read master/slave status
+ * @phydev: target phy_device struct
+ *
+ * Description: Read the Master/Slave configuration and status
+ * from 10GBASE-T control/status registers (MMD 7, Reg 0x0020 and 0x0021).
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int genphy_c45_read_master_slave(struct phy_device *phydev)
+{
+ int val;
+
+ phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_10GBT_CTRL_MS_ENABLE) {
+ if (val & MDIO_AN_10GBT_CTRL_MS_VALUE)
+ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+ else
+ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+ } else {
+ if (val & MDIO_AN_10GBT_CTRL_MS_PORT_TYPE)
+ phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+ else
+ phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+ }
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+ if (val < 0)
+ return val;
+
+ if (val & MDIO_AN_10GBT_STAT_MS_FAULT) {
+ phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
+ } else if (phydev->link) {
+ if (val & MDIO_AN_10GBT_STAT_MS_RES)
+ phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+ else
+ phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+ } else {
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+ }
+
+ return 0;
+}
+
/**
* genphy_c45_aneg_done - return auto-negotiation complete status
* @phydev: target phy_device struct
@@ -1214,6 +1305,10 @@ int genphy_c45_read_status(struct phy_device *phydev)
ret = genphy_c45_baset1_read_status(phydev);
if (ret < 0)
return ret;
+ } else {
+ ret = genphy_c45_read_master_slave(phydev);
+ if (ret < 0)
+ return ret;
}
phy_resolve_aneg_linkmode(phydev);
@@ -1247,6 +1342,14 @@ int genphy_c45_config_aneg(struct phy_device *phydev)
if (ret > 0)
changed = true;
+ if (!genphy_c45_baset1_able(phydev)) {
+ 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..06f4bc3c20c7 100644
--- a/include/uapi/linux/mdio.h
+++ b/include/uapi/linux/mdio.h
@@ -332,8 +332,13 @@
#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_MS_FAULT 0x8000 /* Master/slave fault */
+#define MDIO_AN_10GBT_STAT_MS_RES 0x4000 /* Master/slave resolution (1=Master) */
#define MDIO_AN_10GBT_STAT_LP2_5G 0x0020 /* LP is 2.5GBT capable */
#define MDIO_AN_10GBT_STAT_LP5G 0x0040 /* LP is 5GBT capable */
#define MDIO_AN_10GBT_STAT_LPTRR 0x0200 /* LP training reset req. */
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v8 3/4] net: phy: realtek: add support for RTL8261C_CG
2026-07-02 3:20 [PATCH net-next v8 0/4] Add support for RTL8261C_CG javen
2026-07-02 3:20 ` [PATCH net-next v8 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
2026-07-02 3:20 ` [PATCH net-next v8 2/4] net: phy: c45: add setup and read master/slave helpers javen
@ 2026-07-02 3:20 ` javen
2026-07-02 11:40 ` Nicolai Buchwitz
2026-07-02 3:20 ` [PATCH net-next v8 4/4] net: phy: realtek: load firmware " javen
3 siblings, 1 reply; 8+ messages in thread
From: javen @ 2026-07-02 3:20 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.
This patch introduces a distinct family of handlers (probe, get_features,
config_aneg, read_status, config_intr, handle_interrupt).
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
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()
Changes in v6:
- when PHY_INTERRUPT_DISABLE, clear IMR and ISR
- if AUTONEG_DISABLE, nothing need to do in rtl8261x_config_aneg
- add rtl8261x_read_status, support 1G speed
Changes in v7:
- remove RTL8261X_IMR and RTL8261X_ISR, duplicated definition
- modify commit message
- continue with default behavior when meet unknown sub_phy_id
- change the internal order of rtl8261x_read_status
- expand RTL8261X_INT_MASK_DEFAULT
- add the handle for ADVERTISE_1000HALF in rtl8261x_config_aneg
Changes in v8:
- no changes
---
drivers/net/phy/realtek/realtek_main.c | 186 +++++++++++++++++++++++++
1 file changed, 186 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index 27268811f564..ef3700894ebf 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_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 | \
+ RTL8261X_INT_AUTONEG_ERROR | \
+ RTL8261X_INT_JABBER)
+
+#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,150 @@ 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_warn(phydev, "Unknown sub_id 0x%02x, default behavior\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_read_status(struct phy_device *phydev)
+{
+ int ret, val;
+
+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_STAT1000));
+ if (val < 0)
+ return val;
+
+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
+ }
+
+ ret = genphy_c45_read_status(phydev);
+ if (ret < 0)
+ return ret;
+
+ 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, RTL8221B_VND2_INSR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
+ RTL8261X_INT_MASK_DEFAULT);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+ 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, RTL8221B_VND2_INSR);
+ 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 (phydev->autoneg == AUTONEG_DISABLE)
+ return 0;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ phydev->advertising))
+ adv_1g = ADVERTISE_1000FULL;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ phydev->advertising))
+ adv_1g |= ADVERTISE_1000HALF;
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_CTRL1000),
+ ADVERTISE_1000FULL | ADVERTISE_1000HALF,
+ 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 +3175,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 = rtl8261x_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 related [flat|nested] 8+ messages in thread
* [PATCH net-next v8 4/4] net: phy: realtek: load firmware for RTL8261C_CG
2026-07-02 3:20 [PATCH net-next v8 0/4] Add support for RTL8261C_CG javen
` (2 preceding siblings ...)
2026-07-02 3:20 ` [PATCH net-next v8 3/4] net: phy: realtek: add support for RTL8261C_CG javen
@ 2026-07-02 3:20 ` javen
3 siblings, 0 replies; 8+ messages in thread
From: javen @ 2026-07-02 3:20 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.
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
Changes in v6:
- replace rtl_phy_write_mmd_bits with phy_modify_mmd, keep mdio lock
- check msb and lsb at the beginning of rtl8261x_fw_execute_entry()
- add comments on rtl8261x_config_init()
Changes in v7:
- no changes
Changes in v8:
- remove some phydev_err message in rtl8261x_fw_execute_entry() and
rtl8261x_config_init()
---
drivers/net/phy/realtek/realtek_main.c | 214 +++++++++++++++++++++++++
1 file changed, 214 insertions(+)
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index ef3700894ebf..10c8abfebe84 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,43 @@
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 RTL8261X_MAX_MMD_DEV 31
+
+#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 */
+ __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 until equal (1) or not equal (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 +339,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 +386,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 +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;
@@ -407,6 +460,152 @@ static int rtl8261x_read_status(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;
+}
+
+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, 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;
+
+ if (dev > RTL8261X_MAX_MMD_DEV) {
+ phydev_err(phydev, "invalid firmware MMD device: dev=%u\n", dev);
+ return -EINVAL;
+ }
+
+ if (msb > 15 || lsb > msb) {
+ phydev_err(phydev, "invalid firmware bits: msb=%u, lsb=%u\n", msb, lsb);
+ return -EINVAL;
+ }
+
+ switch (entry->type) {
+ case OP_WRITE:
+ ret = phy_modify_mmd(phydev, dev, addr,
+ GENMASK(msb, lsb), (value << lsb) & GENMASK(msb, lsb));
+ if (ret)
+ 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)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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;
@@ -484,6 +683,20 @@ 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;
+
+ /* The firmware parameters are preserved across IEEE soft resets and
+ * suspend/resume cycles. Reloading is only necessary after a power
+ * cycle or hard reset.
+ */
+ if (priv->fw_name && !priv->fw_loaded)
+ return rtl8261x_fw_load(phydev);
+
+ return 0;
+}
+
static int rtl821x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -3179,6 +3392,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 = rtl8261x_read_status,
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v8 3/4] net: phy: realtek: add support for RTL8261C_CG
2026-07-02 3:20 ` [PATCH net-next v8 3/4] net: phy: realtek: add support for RTL8261C_CG javen
@ 2026-07-02 11:40 ` Nicolai Buchwitz
0 siblings, 0 replies; 8+ messages in thread
From: Nicolai Buchwitz @ 2026-07-02 11:40 UTC (permalink / raw)
To: javen
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, netdev, linux-kernel, daniel, vladimir.oltean
Hi Javen
On 2.7.2026 05:20, 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.
> This patch introduces a distinct family of handlers (probe,
> get_features,
> config_aneg, read_status, config_intr, handle_interrupt).
>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
> 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()
>
> Changes in v6:
> - when PHY_INTERRUPT_DISABLE, clear IMR and ISR
> - if AUTONEG_DISABLE, nothing need to do in rtl8261x_config_aneg
> - add rtl8261x_read_status, support 1G speed
>
> Changes in v7:
> - remove RTL8261X_IMR and RTL8261X_ISR, duplicated definition
> - modify commit message
> - continue with default behavior when meet unknown sub_phy_id
> - change the internal order of rtl8261x_read_status
> - expand RTL8261X_INT_MASK_DEFAULT
> - add the handle for ADVERTISE_1000HALF in rtl8261x_config_aneg
>
> Changes in v8:
> - no changes
> ---
> drivers/net/phy/realtek/realtek_main.c | 186 +++++++++++++++++++++++++
> 1 file changed, 186 insertions(+)
>
> diff --git a/drivers/net/phy/realtek/realtek_main.c
> b/drivers/net/phy/realtek/realtek_main.c
> index 27268811f564..ef3700894ebf 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_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 | \
> + RTL8261X_INT_AUTONEG_ERROR | \
> + RTL8261X_INT_JABBER)
> +
> +#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,150 @@ 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_warn(phydev, "Unknown sub_id 0x%02x, default behavior\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_read_status(struct phy_device *phydev)
> +{
> + int ret, val;
> +
> + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
> + val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
> + RTL822X_VND2_C22_REG(MII_STAT1000));
> + if (val < 0)
> + return val;
> +
> + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
> + }
> +
> + ret = genphy_c45_read_status(phydev);
> + if (ret < 0)
> + return ret;
> +
> + 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, RTL8221B_VND2_INSR);
> + if (ret < 0)
> + return ret;
> +
> + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
> + RTL8261X_INT_MASK_DEFAULT);
> + if (ret < 0)
> + return ret;
> + } else {
> + ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER, 0);
> + if (ret < 0)
> + return ret;
> +
> + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
> + 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,
> RTL8221B_VND2_INSR);
> + 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 (phydev->autoneg == AUTONEG_DISABLE)
> + return 0;
> +
> + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
> + phydev->advertising))
> + adv_1g = ADVERTISE_1000FULL;
> + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
> + phydev->advertising))
> + adv_1g |= ADVERTISE_1000HALF;
> +
> + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
> + RTL822X_VND2_C22_REG(MII_CTRL1000),
> + ADVERTISE_1000FULL | ADVERTISE_1000HALF,
> + 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 +3175,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 = rtl8261x_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,
> },
> };
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Thanks
Nicolai
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v8 1/4] net: phy: c45: add genphy_c45_soft_reset()
2026-07-02 3:20 ` [PATCH net-next v8 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
@ 2026-07-02 12:12 ` Maxime Chevallier
2026-07-03 1:40 ` Javen
0 siblings, 1 reply; 8+ messages in thread
From: Maxime Chevallier @ 2026-07-02 12:12 UTC (permalink / raw)
To: javen, andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
freddy_gu, nb
Cc: netdev, linux-kernel, daniel, vladimir.oltean
Hi Javen,
On 7/2/26 05:20, 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.
>
> 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
>
> Changes in v6:
> - increase timeout to 600ms
>
> Changes in v7:
> - no changes
>
> Changes in v8:
> - 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..60d044156a83 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, 600000, true);
> +}
> +EXPORT_SYMBOL_GPL(genphy_c45_soft_reset);
Can you name it genphy_c45_pma_soft_reset() instead ? That's the common
naming pattern for C45 generic helpers targetting the PMAPMD MMD.
This will avoid some confusion as some in-tree drivers also configure the
MDIO_CTRL1_RESET register, but from the PHYXS or PCS MMDs.
Thanks,
Maxime
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH net-next v8 1/4] net: phy: c45: add genphy_c45_soft_reset()
2026-07-02 12:12 ` Maxime Chevallier
@ 2026-07-03 1:40 ` Javen
0 siblings, 0 replies; 8+ messages in thread
From: Javen @ 2026-07-03 1:40 UTC (permalink / raw)
To: Maxime Chevallier, andrew@lunn.ch, hkallweit1@gmail.com,
linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, 顾晓军,
nb@tipi-net.de
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
daniel@makrotopia.org, vladimir.oltean@nxp.com
>
>Hi Javen,
>
>On 7/2/26 05:20, 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.
>>
>> 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
>>
>> Changes in v6:
>> - increase timeout to 600ms
>>
>> Changes in v7:
>> - no changes
>>
>> Changes in v8:
>> - 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..60d044156a83 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, 600000, true); }
>> +EXPORT_SYMBOL_GPL(genphy_c45_soft_reset);
>
>Can you name it genphy_c45_pma_soft_reset() instead ? That's the common
>naming pattern for C45 generic helpers targetting the PMAPMD MMD.
>
>This will avoid some confusion as some in-tree drivers also configure the
>MDIO_CTRL1_RESET register, but from the PHYXS or PCS MMDs.
>
Sure. Thanks for review.
BRs,
Javen
>Thanks,
>
>Maxime
>
>>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-03 1:41 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-02 3:20 [PATCH net-next v8 0/4] Add support for RTL8261C_CG javen
2026-07-02 3:20 ` [PATCH net-next v8 1/4] net: phy: c45: add genphy_c45_soft_reset() javen
2026-07-02 12:12 ` Maxime Chevallier
2026-07-03 1:40 ` Javen
2026-07-02 3:20 ` [PATCH net-next v8 2/4] net: phy: c45: add setup and read master/slave helpers javen
2026-07-02 3:20 ` [PATCH net-next v8 3/4] net: phy: realtek: add support for RTL8261C_CG javen
2026-07-02 11:40 ` Nicolai Buchwitz
2026-07-02 3:20 ` [PATCH net-next v8 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